## Make output directory
outdir <- "~/multiOmic_benchmark/report/output/20191127_tcellTrajectory/"
ifelse(!dir.exists(outdir), dir.create(outdir), FALSE)
[1] FALSE

Based on the results of my benchmark, I set out to align expression and accessibility profiles from the F74 developing thymus dataset to detect changes in accessibility along pseudotime trajectories. While the benchmark was based on the task of label propagation, I here use the two most faithful methods (Seurat CCA and Conos) to achieve a common embedding of ATAC-seq and RNA-seq cells.

Load datasets.

rna.sce <- readRDS("~/my_data/F74_RNA_seurat_processed.RDS")
atac.sce <- readRDS("~/my_data/F74_ATAC_snapAtac_processed_bgmat.RDS")

## Re-normalize RNA data
seu.rna <- as.Seurat(rna.sce, counts = "counts")
seu.rna <- NormalizeData(seu.rna)
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
logcounts(rna.sce) <- seu.rna@assays$RNA@data

Filter genes with zero variance

rna.gene.var <- as.matrix(counts(rna.sce)) %>% rowVars()
atac.gene.var <- as.matrix(counts(atac.sce)) %>% rowVars()

rna.sce <- rna.sce[which(rna.gene.var > 0),]
atac.sce <- atac.sce[which(atac.gene.var > 0),]

rna.sce; atac.sce
class: SingleCellExperiment 
dim: 24510 8321 
metadata(0):
assays(3): counts cpm logcounts
rownames(24510): RP11-34P13.3 RP11-34P13.7 ... AC233755.1 AC240274.1
rowData names(0):
colnames(8321): AAACCTGAGTTCGATC_1 AAACCTGCAAGTTGTC_1 ... TTTGTCAAGCTGAACG_2 TTTGTCAGTATTAGCC_2
colData names(1): annotation
reducedDimNames(0):
spikeNames(0):
class: SingleCellExperiment 
dim: 31122 5793 
metadata(0):
assays(3): counts cpm logcounts
rownames(31122): A1BG A1BG-AS1 ... ZYX ZZEF1
rowData names(0):
colnames(5793): AAACGAAAGTGAACCG-1 AAACGAACATCGGCCA-1 ... TTTGTGTTCGATCGCG-1 TTTGTGTTCTGAGTAC-1
colData names(28): orig.ident nCount_ATAC ... nFeature_ACTIVITY ident
reducedDimNames(2): LSI UMAP
spikeNames(0):

Integration of T cells clusters

I re-run the integration based on the T cell subset. To select cells from the scATAC dataset, I take the SnapATAC clusters that best correspond to T-cells, based on label transfer.

tcells.sce.atac <- atac.sce[,which(as.numeric(atac.sce$seurat_clusters) %in% c(1:9))]

tcells.rna.ix <- which(rna.sce$annotation %in% c("DN","DP (Q)", "DP (P)", "SP (1)", "SP (2)"))
tcells.sce.rna <- rna.sce[,tcells.rna.ix]

tcells.sce.list <- list(RNA=tcells.sce.rna, ATAC=tcells.sce.atac)

## Make color palette 4 cell types
cell.types <- as.character(unique(tcells.sce.rna$annotation))
cell.type.pal <- brewer.pal(length(cell.types), "Set1") %>% rev() %>% setNames(cell.types)

Next, I select genes on which to perform integration. I take the union of the most variable features in the RNA dataset and the most covered features in the ATAC dataset

hcg.atac <- select_highlyCovered(tcells.sce.list$ATAC, frac_cells = 0.2)
hvg.rna <- select_highlyVariable(tcells.sce.list$RNA)
Calculating gene means
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variance to mean ratios
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
seu.rna <- FindVariableFeatures(seu.rna, nfeatures = 2000,
                                # selection.method = "mvp", dispersion.cutoff=c(0.7, 100), mean.cutoff=c(0.02, 3)
                                )
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
hvg.rna <- VariableFeatures(seu.rna)

VariableFeaturePlot(seu.rna)

UpSetR::upset(UpSetR::fromList(list(HVG.RNA=hvg.rna, HCG.ATAC=hcg.atac)))

Remove cell cycle genes, that might interfere with pseudotime ordering

cell_cycle_genes <- read.table("~/annotations/cell_cycle_genes.tsv")$V1

integrate_features_union <- union(hvg.rna, hcg.atac)
integrate_features_union <- setdiff(integrate_features_union, cell_cycle_genes) 

## Select features in both datasets
integrate_features_union <- intersect(integrate_features_union, intersect(rownames(tcells.sce.list$ATAC), rownames(tcells.sce.list$RNA))) 

Visualize T cells in RNA dataset

tcells.seu.list <- map(tcells.sce.list, ~ as.Seurat(.x))
All keys should be one or more alphanumeric characters followed by an underscore '_', setting key to LSI_All keys should be one or more alphanumeric characters followed by an underscore '_', setting key to UMAP_
tcells.RNA.union <- tcells.seu.list$RNA
VariableFeatures(tcells.RNA.union) <- integrate_features_union
tcells.RNA.union <- ScaleData(tcells.RNA.union) %>% RunPCA() %>% RunUMAP(dims=1:40)
Centering and scaling data matrix

  |                                                                                                                      
  |                                                                                                                |   0%
  |                                                                                                                      
  |======================                                                                                          |  20%
  |                                                                                                                      
  |=============================================                                                                   |  40%
  |                                                                                                                      
  |===================================================================                                             |  60%
  |                                                                                                                      
  |==========================================================================================                      |  80%
  |                                                                                                                      
  |================================================================================================================| 100%
The following 176 features requested have zero variance (running reduction without them): TM4SF18, MEOX2, ASGR2, DMRT2, KIR2DL4, CACNA1B, FREM2, MYZAP, TOX3, PRIMA1, PPP2R2B, PTPRZ1, CMTM5, TREM2, CALB2, TMTC1, CDH6, PKHD1L1, ISLR2, KRT7, MYH8, DCX, CCSER1, MSLN, CDH19, EYA4, P2RY12, PLA2G2D, ADAMTS16, KCNJ5, PAPPA, QRFPR, ADGRG2, NFIA-AS2, LUZP2, NALCN, FABP7, TSPEAR-AS1, GPR1, CD300LF, A4GALT, AP000439.1, CEACAM3, SLITRK2, KIR3DL1, AC147651.1, MUC15, FFAR4, C11orf53, KLK11, WIF1, CFHR1, TMEM233, FOLR3, GRID2, GALNT15, FAM19A1, TAC1, PTCHD4, CCL11, ZNF385B, GABRA1, ADH1B, LINC01048, LMOD1, TNNT1, ACTL6B, SEMA3E, HOXD-AS2, NTF4, SLC35F1, MANEAL, FEV, RAB3C, SYT9, HMCN2, ZFHX4-AS1, PCSK9, MMP12, ABCB11, AC002066.1, ADAM23, ADCY5, ADGB, ADGRB1, ANKRD29, ATP2B2, C4orf45, CCDC33, CDH18, CDK15, CERS1, CFAP161, CLRN1-AS1, CNBD1, CNGB3, CNTN5, COL8A2, CTNND2, DCC, DSCAM, EPHA6, FGF12, FSTL5, GABBR2, GABRB1, GABRG3, GALNT13, GHR, GLIS3, GNG12-AS1, GNGT1, GRIN2B, GRM1, GRM8, HMCN1, HPSE2, HS3ST5, HS6ST3, IL1RAPL2, ITGBL1, KCNB1, KCNH7, LINC00639, LINC01169, LINC01170, LINC01182, LINC01317, LINC01505, LINGO1, LINGO2, LRRC4C, LRRTM4, LY75-CD302, MAP6, MLIP, MYRIP, NEBL, NKAIN2, NKAIN3, NRG1, OPCML, PCDH15, PCDHGA4, PDZRN4, PIK3C2G, PKNOX2, PNPLA1, POU6F2, PPFIA2, PRR5-ARHGAP8, PTPN5, PTPRT, RALYL, RERG, RGS7, RIMS1, RIN2, RYR3, SHANK2, SNTG1, SORCS3, STON1-GTF2A1L, SYN2, TENM2, THSD4, TRPC6, TSPEAR, TVP23C-CDRT4, UNC80, USH1C, VAX2, VWA3B, XKR4, ZBTB7C, ZNF804BPC_ 1 
Positive:  SATB1, PTPRC, MTSS1, APBB1IP, LYST, SH2D1A, CAMK4, LBH, FBLN5, LEF1 
       PLEKHG1, VOPP1, CD247, MXD1, TCF12, ARHGEF7, ALDH1A2, NFATC3, TBC1D19, SYTL3 
       ADAMTS17, ANO6, DAPK1, CALN1, THEMIS, PITPNM2, NLGN4X, MAPRE2, GALNT7, ZNF280D 
Negative:  ENO1, GSTP1, FABP5, TMSB10, SMS, PKM, NME4, VIM, RPL37A, YWHAQ 
       NCL, IGFBP2, CAPG, NDUFA12, TRDC, PGK1, PARVB, LDHB, ATOX1, SELL 
       CDC123, NUDC, IGLL1, UBE2N, FXYD2, GMPS, C20orf27, SLC25A39, ANXA1, C12orf75 
PC_ 2 
Positive:  RPL37A, EIF3H, LDHB, TBCA, IL32, SERGEF, SMPD3, ITGAE, AATF, FBLN5 
       NDUFA12, SOD1, DNAJC15, C12orf75, MRPL33, TMSB10, HNRNPC, CCDC57, SMCO4, GDI2 
       OLA1, ALDH1A2, COX7A2L, CST3, CYSTM1, ATOX1, GYPC, PDCD6, FABP5, RALY 
Negative:  MBNL1, ITPR2, PTPRC, MTRNR2L12, ADAM10, MSI2, CDK6, SELL, JCHAIN, RNF213 
       MME, TCF12, BPTF, BCL11B, NIPBL, MACF1, PIK3R1, HIVEP3, SOCS2, GALNT7 
       IKZF2, RUNX1, SPTBN1, PRRC2B, BCL2, DIAPH1, MYCBP2, SLC38A1, MBP, RUFY3 
PC_ 3 
Positive:  HLA-B, TOX2, COTL1, CTSW, KLRB1, CD40LG, SIRPG, GZMM, CLDN1, CD74 
       ITM2A, GBP2, BACH2, HPGD, PDE4D, TNFRSF1B, S100A10, CLEC2D, XCL1, GFOD1 
       CRTAM, MATK, DENND2D, PDCD1, GIMAP4, STAT1, ZNF683, CD226, HLA-A, TNFRSF25 
Negative:  TFDP2, JCHAIN, ATP6AP1L, DEFA6, MME, GALNT2, PCGF5, ADGRG1, GLIPR1, MSI2 
       NINL, CEP70, CDK6, PTPN2, FXYD2, TRDC, GSTP1, SELL, SOCS2, RGPD3 
       SMPD3, FABP5, LYST, SSBP2, PITPNM2, DLEU7, UBE2E1, NUCB2, PPP1R1C, BCL11A 
PC_ 4 
Positive:  SMPD3, C12orf75, TUBA1C, LCP1, HIST1H2AB, SMS, SMCO4, GMPS, FABP5, XPO1 
       IGFBP2, CCND3, TAF15, RGS3, SREBF2, YWHAQ, TMSB15A, ABCD3, NCL, PHIP 
       GNAS, EPB41L2, SYNE2, STAG2, CNTLN, VIM, NUP210, MCTP1, NUDC, NFATC3 
Negative:  JCHAIN, SELL, DEFA6, SOCS2, GLIPR1, ATP6AP1L, MME, RGPD3, XG, DPP4 
       IFI6, ENAM, GNAL, EVL, NEGR1, FRMPD2, EVA1A, PIK3CD, GALNT2, NDST3 
       MBP, RNF144A, FXYD2, COL1A2, SMIM24, NDFIP2, OXNAD1, ACTN1, LSP1, LINC00861 
PC_ 5 
Positive:  HPGD, BACH2, TOX2, ITM2A, GZMM, ST6GAL1, ARAP2, CD96, TGFBR2, LZTFL1 
       VIM, EVL, CD44, SATB1, IL2, TUSC3, CYTIP, PDE4D, MAD1L1, SLC2A3 
       ARHGAP31, GPR183, PGK1, ANKRD44, STK4, BCL2, GNG2, IKZF1, ICOS, ETS1 
Negative:  XCL1, TNFRSF9, CTSW, XCL2, GBP2, TNFRSF1B, S100A4, GNG4, NPW, CD74 
       HOPX, IGFBP4, CD151, SH3BGRL2, NFATC1, LYST, ATP9A, NR4A2, LINC01480, CLEC2D 
       MIR3142HG, LINC01281, PREX1, HDAC9, PHACTR1, MAP7, CST3, CXCR3, PITPNM2, PDCD1 
The default method for RunUMAP has changed from calling Python UMAP via reticulate to the R-native UWOT using the cosine metric
To use Python UMAP via reticulate, set umap.method to 'umap-learn' and metric to 'correlation'
This message will be shown once per session09:11:47 UMAP embedding parameters a = 0.9922 b = 1.112
09:11:47 Read 7101 rows and found 40 numeric columns
09:11:47 Using Annoy for neighbor search, n_neighbors = 30
09:11:47 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
09:11:48 Writing NN index file to temp file /tmp/RtmpIaaTXb/file9c876abf67
09:11:48 Searching Annoy index using 1 thread, search_k = 3000
09:11:51 Annoy recall = 100%
09:11:52 Commencing smooth kNN distance calibration using 1 thread
09:11:55 Initializing from normalized Laplacian + noise
09:11:55 Commencing optimization for 500 epochs, with 314138 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
09:12:14 Optimization finished
DimPlot(tcells.RNA.union, group.by = "annotation", label=TRUE) + ggtitle("RNA - feature union")
Using `as.character()` on a quosure is deprecated as of rlang 0.3.0.
Please use `as_label()` or `as_name()` instead.
This warning is displayed once per session.

Visualize markers

t.cell.markers <- list(known.markers = c("CD34", "IGLL1", "TRGC2", "TRDC", "PTCRA", "TRBC2", "TRAC", "CD4", "CD8A", "CD8B"),
                       chemokine.receptors = c("CCR9", "CCR7"),
                       tcr.activation = c("CD5", "CD27"),
                       proliferation=c("PCNA", "CDK1", "MKI67"),
                       cyclin.D = c("CCND2", "CCND3"),
                       recombination=c("RAG1", "RAG2"),
                       apoptosis=c("HRK","BMF", "TP53INP1"),
                       stage.markers = c("ST18", "HIVEP3", "RGPD3", "SMPD3", "AQP3", "RORC", "SATB1", "TOX2")
                       ) 
# FeaturePlot(tcells.RNA.ref, features = t.cell.markers$known.markers, cols = viridis::viridis(n=10))
FeaturePlot(tcells.RNA.union, features = t.cell.markers$known.markers, cols = viridis::viridis(n=10))

Visualize T cells in ATAC dataset

Colored by clusters called with SnapATAC

tcells.ATAC.union <- tcells.seu.list$ATAC
# tcells.ATAC.union <- NormalizeData(tcells.ATAC.union)
VariableFeatures(tcells.ATAC.union) <- integrate_features_union
tcells.ATAC.union <- RunLSI(tcells.ATAC.union, n=50, scale.max = NULL)
RunLSI is being moved to Signac. Equivalent functionality can be achieved via the Signac::RunTFIDF and Signac::RunSVD functions; for more information on Signac, please see https://github.com/timoast/SignacRunLSI is being moved to Signac. Equivalent functionality can be achieved via the Signac::RunTFIDF and Signac::RunSVD functions; for more information on Signac, please see https://github.com/timoast/SignacRunLSI is being moved to Signac. Equivalent functionality can be achieved via the Signac::RunTFIDF and Signac::RunSVD functions; for more information on Signac, please see https://github.com/timoast/SignacPerforming TF-IDF normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Running SVD on TF-IDF matrix
Scaling cell embeddings
Cannot add objects with duplicate keys (offending key: LSI_), setting key to 'lsi_'
tcells.ATAC.union <- RunUMAP(tcells.ATAC.union, reduction = "lsi", dims = 1:50)
10:10:05 UMAP embedding parameters a = 0.9922 b = 1.112
10:10:05 Read 4977 rows and found 50 numeric columns
10:10:05 Using Annoy for neighbor search, n_neighbors = 30
10:10:05 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
10:10:06 Writing NN index file to temp file /tmp/RtmpIaaTXb/file9c861ce1d48
10:10:06 Searching Annoy index using 1 thread, search_k = 3000
10:10:08 Annoy recall = 100%
10:10:11 Commencing smooth kNN distance calibration using 1 thread
10:10:14 Initializing from normalized Laplacian + noise
10:10:14 Commencing optimization for 500 epochs, with 172060 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
10:10:26 Optimization finished
Cannot add objects with duplicate keys (offending key: UMAP_), setting key to 'umap_'
DimPlot(tcells.ATAC.union, reduction = "umap", group.by = "seurat_clusters", label = TRUE) + ggtitle("ATAC gmat")

Run CCA

Makes imputed transcriptome profile for the ATAC-seq cells to allow co-embedding

sce.list <- tcells.sce.list
reference = "RNA"
query = "ATAC" 
seurat.list <- imap(sce.list, ~ as.Seurat(.x, assay=.y))
seurat.list <- imap(seurat.list, ~ RenameCells(.x, add.cell.id=.y))
## Scale data
seurat.list <- map(seurat.list, ~ ScaleData(.x))
## Calculate CCA anchors
transfer.anchors <- FindTransferAnchors(reference = seurat.list[[reference]], 
                                        query = seurat.list[[query]],
                                        features = integrate_features_union, 
                                        reduction = "cca")

## Impute expression profiles for ATAC cells (for all genes, not just integration features)
refdata <- GetAssayData(seurat.list$RNA, assay = "RNA", slot = "data")
imputation <- TransferData(anchorset = transfer.anchors, refdata = refdata, weight.reduction = seurat.list$ATAC[["LSI"]])

## Merge datasets and co-embed
seurat.list$ATAC[["RNA"]] <- imputation
coembed <- merge(x = seurat.list$RNA, y = seurat.list$ATAC)

coembed <- ScaleData(coembed, features = integrate_features_union, do.scale = FALSE)
coembed <- RunPCA(coembed, features = integrate_features_union, verbose = FALSE)
coembed <- RunUMAP(coembed, dims = 1:30)

coembed <- AddMetaData(coembed, metadata = ifelse(colnames(coembed) %in% colnames(seurat.list[[reference]]), reference, query), col.name = "tech")

Transfer labels on ATAC dataset

celltype.predictions <- TransferData(anchorset = transfer.anchors, 
                                     refdata = seurat.list[[reference]]$annotation, 
                                     weight.reduction = seurat.list$ATAC[["LSI"]])

coembed <- AddMetaData(coembed, metadata = celltype.predictions)
coembed@meta.data %<>%
  rownames_to_column() %>%
  dplyr::mutate(annotation=ifelse(is.na(predicted.id) , annotation, NA)) %>%
  column_to_rownames()

coembed@meta.data <-
  coembed@meta.data %>%
  rownames_to_column() %>%
  dplyr::mutate(annotation=ifelse(is.na(annotation) & prediction.score.max > 0.5, predicted.id, annotation)) %>%
  dplyr::mutate(annotation=ifelse(annotation=="SP (2)", NA, annotation)) %>%
  column_to_rownames()
CombinePlots(
  list(DimPlot(coembed, group.by = c("predicted.id"), cols = cell.type.pal) + ggtitle("prediction"),
  DimPlot(coembed, group.by = c("annotation"), cols = cell.type.pal) + ggtitle("Original + prediction")),
  legend = "top"
  )

FeaturePlot(coembed, features = "prediction.score.max", cells = which(coembed$tech=="ATAC")) + scale_color_viridis_c()
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.

Run Pseudotime analysis

Identify cell of origin among the DN cells based on expression of IGLL1 and CD34

FeaturePlot(coembed, features = c("IGLL1", "CD34"), split.by = "tech", slot = "data", cols = viridis::viridis(n=100))

cell.oo <-
  coembed@meta.data %>% 
  rownames_to_column("cell") %>%
  mutate(IGLL1=coembed@assays$RNA@counts["IGLL1",cell]) %>%
  select(cell, annotation, IGLL1) %>%
  arrange(-IGLL1) %>%
  filter(annotation=="DN") %>%
  top_n(1, IGLL1) %>%
  pull(cell)

coembed@reductions$umap@cell.embeddings %>%
  as.tibble(rownames="cell") %>%
  mutate(cell.oo = ifelse(cell %in% cell.oo, T, F)) %>%
  ggplot(aes(UMAP_1, UMAP_2)) +
  geom_point(color="grey50") +
  geom_point(data=. %>% filter(cell.oo),color='red') +
  ggrepel::geom_text_repel(data=. %>% filter(cell.oo), aes(label="cell of origin"), color='red') +
  theme_cowplot() 

coembed <- AddMetaData(coembed, ifelse(colnames(coembed)==cell.oo, TRUE, FALSE), col.name = "iroot_cell")
merged.sce <- SingleCellExperiment(list(counts=coembed@assays$RNA@counts, logcounts=coembed@assays$RNA@data), colData=coembed@meta.data[, c("annotation", "tech", "iroot_cell")],
                     reducedDims = map(coembed@reductions, ~ .x@cell.embeddings))

saveRDS(object = merged.sce, "~/my_data/Tcells_CCA_integration_20191203.RDS")
saveRDS(object = integrate_features_union, "~/my_data/intFeatures_Tcells_CCA_integration_20191203.RDS")

I infer pseudotime using the diffusion pseudotime algorithm as implemented in scanpy. Making an R/reticulate wrapper for this function would be nice, but for now, see multiOmic_benchmark/DPT_tcells.ipynb.

Read scanpy output and save in R object.

dpt <- read.csv('~/my_data/Tcells_CCA_integration_20191127_scanpy_dpt.csv') %>%
  select(X, dpt_pseudotime)

coembed <- AddMetaData(coembed, column_to_rownames(dpt, 'X'))
saveRDS(coembed, "~/my_data/Tcells_CCA_integration_seurat_20191203.Rmd")
coembed <- readRDS("~/my_data/Tcells_CCA_integration_seurat_20191203.Rmd")

Visualize pseudotime

FeaturePlot(coembed, reduction = "umap", feature = "dpt_pseudotime", split.by = "tech", col=viridis::viridis(10)) 

Save figure

coembed.umaps.pl <- plot_grid(
  DimPlot(coembed, group.by = c("tech")) + theme(legend.position = "top"),
  DimPlot(coembed, group.by = c("annotation"), cols = cell.type.pal, label = TRUE, label.size = 5) + theme(legend.position = "none"),
  FeaturePlot(coembed, reduction = "umap", feature = "dpt_pseudotime") + scale_color_viridis_c(name="Diffusion\npseudotime") + ggtitle(""),
  nrow=1, ncol=3, rel_widths = c(1,1,1.2),
  labels = c("A", "B", "C")
) 
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
coembed.umaps.pl +
  ggsave(paste0(outdir, "coembed_umaps.png"), width=12, height = 4)

coembed@meta.data %>%
  dplyr::mutate(`DPT rank`=dense_rank(dpt_pseudotime)) %>%
  ggplot(aes(`DPT rank`)) +
  geom_histogram(aes(fill=annotation), bins=50) +
  facet_grid(annotation~tech, scales="free_y") +
  theme_bw(base_size = 16) +
  scale_fill_manual(values = cell.type.pal)

Check expression of markers along pseudotime

coembed@assays$RNA@data[t.cell.markers$known.markers, ] %>%
  as.matrix() %>%
  reshape2::melt(varnames=c("gene", "cell")) %>%
  left_join(coembed@meta.data[,"dpt_pseudotime", drop=F] %>% rownames_to_column("cell")) %>%
  mutate(pseudotime.rank=dense_rank(dpt_pseudotime)) %>%
  group_by(gene) %>%
  arrange(pseudotime.rank) %>%
  # mutate(value=(value-min(value))/max(value)-min(value)) %>%
  mutate(value=zoo::rollmean(value, k=5, fill=NA)) %>% 
  # mutate(value=(value-mean(value))/sd(value)) %>%
  ungroup() %>%
  mutate(gene=factor(gene, levels=rev(unique(gene)))) %>%
  ggplot(aes(pseudotime.rank, gene, fill=value)) +
  geom_tile() +
  scale_fill_viridis_c(name="log expression") +
  theme_bw(base_size = 16) +
  theme(panel.border = element_blank(), panel.grid = element_blank())

Check accessibility of markers along pseudotime

FeaturePlot(coembed, feature=c("CD4"), slot = "data", split.by = "tech")
All cells have the same value (0) of CD4.

Bin pseudotime and visualize cell type composition

dpt.df <- 
  coembed@meta.data %>%
  rownames_to_column("cell") %>%
  dplyr::mutate(dpt_rank=dense_rank(dpt_pseudotime)) %>%
  mutate(dpt_bin=cut(dpt_rank, breaks = 100)) %>%
  mutate(dpt_bin=as.numeric(dpt_bin)) %>%
  select(cell,tech, annotation, prediction.score.max, dpt_bin, dpt_pseudotime, dpt_rank)

cell.type.pl <- dpt.df %>%
  ggplot(aes(dpt_bin, fill = annotation)) +
  # geom_histogram(bins=100) +
  geom_bar() +
  scale_fill_manual(values=cell.type.pal, na.value="grey50") +
  facet_grid(tech~., scales="free_y") +
  xlab("Pseudotime bin") +
  theme_bw(base_size = 16)

cell.type.pl

Correlation between global accessibility and pseudotime ordering.

coembed.umaps.pl <- plot_grid(
  DimPlot(coembed, group.by = c("tech")) + theme_classic(base_size = 16) + theme(legend.position = "top") ,
  DimPlot(coembed, group.by = c("annotation"), cols = cell.type.pal, label = TRUE, label.size = 5) + theme_classic(base_size = 16) + theme(legend.position = "none"),
  FeaturePlot(coembed, reduction = "umap", feature = "dpt_pseudotime") +
    theme_classic(base_size = 16) +
    scale_color_viridis_c(name="Diffusion pseudotime") + ggtitle("") + 
    guides(color=guide_colorbar(barwidth = 10,title.position = "top")) +
    theme(legend.position = "top", legend.justification = "center") ,
  nrow=3, ncol=1, rel_heights = c(1.2,1,1.5)
) 

plot_grid(coembed.umaps.pl, dpt.pl, rel_widths=c(1,1.9)) +
  ggsave(paste0(outdir, "tcells_figure.png"), width=14, height = 10)

Motif analysis

I initially wanted to call peaks from SnapATAC clusters, then build a cell x peak matrix on those detected peaks, but SnapATAC/MACS2 don’t seem to work.

Alternative: load peak matrix from cellranger and add to snap object

filt.peaks <- Read10X_h5("~/my_data/filtered_peak_bc_matrix.h5")
peaks.mat <- str_split(rownames(filt.peaks), pattern = ":|-") %>% map(rbind) %>% purrr::reduce(rbind)
peaks.gr <- GRanges(peaks.mat[,1], IRanges(as.numeric(peaks.mat[,2]), as.numeric(peaks.mat[,3])))
snap.pmat <- createSnapFromPmat(mat=t(filt.peaks[,snap.out@barcode]), barcodes=snap.out@barcode, peaks=peaks.gr)
snap.pmat

Calculating deviations in TF accessibility using ChromVAR. This is a measure of how much is motif accessibility in each cell is enriched compared to all the cells and general cell coverage. While SnapATAC has an wrapper around ChromVAR that outputs the deviation matrix, I just take the code from that function and run every step separately to keep the useful outputs and statistics of chromVAR.

snap.pmat = makeBinary(snap.pmat, "pmat")

obj = snap.pmat
input.mat="pmat"
min.count=10
species="Homo sapiens"
genome=BSgenome.Hsapiens.UCSC.hg38

data.use = obj@pmat
peak.use = obj@peak

ncell = nrow(data.use)

idy = which(Matrix::colSums(data.use) >= min.count)
data.use = data.use[,idy,dropping=TRUE]
    
peak.use = peak.use[idy]

rse <- SummarizedExperiment(
        assays = list(counts = t(data.use)), 
                 rowRanges = peak.use, 
                 colData = DataFrame(Cell_Type=1:nrow(data.use), depth=Matrix::rowSums(data.use))
    );
rse <- addGCBias(rse, genome = genome);
motifs <- getJasparMotifs(collection = "CORE", species=species)
motif_mm <- matchMotifs(motifs, rse, genome = genome);
dev <- computeDeviations(object = rse, annotations = motif_mm);
var <- computeVariability(dev)

Save

rowData(dev) %<>%
  as.tibble(rownames="motif") %>%
  full_join(var) %>%
  column_to_rownames('motif') %>%
  DataFrame()

saveRDS(dev, "~/my_data/Tcells_peaks/Tcells_chromVarOutput.RDS")  
var %>%
  mutate(signif=ifelse(p_value_adj < 0.01, "signif", NA)) %>%
  mutate(rank=rank(-variability)) %>%
  ggplot(aes(rank, variability, color=signif)) +
  geom_point() +
  ggrepel::geom_text_repel(data=. %>% filter(rank < 80 & rank > 50), aes(label=name)) +
  geom_vline(xintercept = 50)

Visualize deviation scores of the most variable motifs, ordered in pseudotime.

sample_dpt_bins.df <- dpt.df %>%
  mutate(cell=str_remove(cell, "^ATAC_")) %>%
  filter(tech=="ATAC") %>%
  arrange(dpt_pseudotime)

motif.topvar <- var %>% rownames_to_column("motif") %>% top_n(50,variability) %>% pull(motif)
tf.topvar <- motif.topvar %>% str_remove(".+_") %>% str_remove("\\(.+|:.+")
mmat.topvar <- dev@assays$data$z[motif.topvar,sample_dpt_bins.df$cell]

rownames(mmat.topvar) <- tf.topvar
smooth.mmat <- apply(mmat.topvar, 1, function(x) zoo::rollmean(x, k=30)) %>% t() 

tiff(paste0(outdir, "chromVAR_motif_heatmap.tiff"), width=900, height = 900)
# pdf(paste0(outdir, "chromVAR_motif_heatmap.pdf"), width=9, height = 10)
smooth.mmat %>%
  # mmat.topvar[,sample_dpt_bins.df$cell] %>%
  pheatmap::pheatmap(show_colnames = F, cluster_cols = F, clustering_distance_rows = "correlation",
                     annotation_col = sample_dpt_bins.df[,c("cell", "annotation", "dpt_pseudotime")] %>% column_to_rownames("cell"), 
                     annotation_colors = list(annotation=cell.type.pal, dpt_pseudotime=viridis::viridis(100)), fontsize = 18, fontsize_row = 14,
                     # color = colorRampPalette(rev(brewer.pal(n = 7, name ="Spectral")))(100))
                     breaks=seq(-3,3, length.out = 100), legend = F, annotation_legend = F,
                     legend_breaks = c(-2, -1, 0, 1, 2, 3), legend_labels = c("-2", "-1", "0", "1","2", "Deviation\n(z-score)")
  )
dev.off()

Compare motif accessibility trend with gene expression trend along pseudotime. I find both examples of correlation between accessibility and TF expression (e.g. RUNX2, ELK3) and anti-correlation (e.g. JUN, ETV6).

counts.topvar <- coembed@assays$RNA@data[tf.topvar[which(tf.topvar %in% rownames(coembed@assays$RNA@data))],dpt.order$cell]
gex.df <- 
  reshape2::melt(as.matrix(counts.topvar), varnames=c("gene", "cell")) %>% 
  # rowid_to_column("dpt_order") %>%
  mutate(data="Gene\nexpression")
mmat.df <- reshape2::melt(mmat.topvar, varnames=c("gene", "cell")) %>%
  # rowid_to_column("dpt_order") %>%
  mutate(cell=str_c("ATAC_", cell)) %>%
  mutate(data="Motif\ndeviation")

plot.tfs <- function(plot.tfs){
  bind_rows(gex.df, mmat.df) %>%
  left_join(dpt.df[, c("cell", "dpt_pseudotime", "annotation")], by="cell") %>%
  mutate(dpt_rank=dense_rank(dpt_pseudotime)) %>%
  drop_na(dpt_pseudotime) %>%
    mutate(data=factor(data, levels=c("Gene\nexpression", "smooth", "Motif\ndeviation"))) %>%
  filter(gene %in% plot.tfs) %>%
  ggplot(aes(dpt_rank, value, color=data)) +
  # geom_point(data=. %>% filter(data!="smooth"), aes(color=annotation), size=0.7, alpha=0.3) +
  geom_smooth( method="loess",span=0.2) +
  facet_grid(data~gene, scales="free") +
  xlab("Pseudotime rank") +
  theme_bw(base_size = 16) 
}

tfs <- c("JUN", "FOSL2", "FOSL1", "FOS")

pdf(paste0(outdir, "TF_plots.pdf"), width = 8, height = 5)
for (tf in tf.topvar) {
  print(plot.tfs(tf))
}
dev.off()

map(list("JUN", "ELK3", "RUNX2", "REL", "FOS", "ETV6", "TCF3"), ~ plot.tfs(.x) + ggsave(paste0(outdir, paste0('TF_plot_',.x,".png")), width = 8, height=5))
tfs <- c("RUNX2", "ELK3","JUN", "ETV6")
plot.tfs(tfs)

tf.list <- map(tfs, ~ plot.tfs(.x))
tf.list <- map(tf.list, ~ .x + theme(legend.position = "none"))
tf.list <- map_if(tf.list, c(TRUE, TRUE, TRUE, FALSE), ~ .x + theme(axis.title.x = element_blank(), axis.ticks.x = element_blank(), axis.text.x=element_blank() ))

tf.list
plot_grid(plotlist = tf.list, align="v", axis="l", nrow=4, ncol=1, rel_heights = c(1,1,1,1.15)) +
  ggsave(paste0(outdir, "TF_pl_fig.png"), height = 11, width = 5)
plot.tfs("TFAP4")
Unequal factor levels: coercing to characterbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding factor and character vector, coercing into character vectorbinding character and factor vector, coercing into character vector

Ways to optimize this analysis

  • Improved motif database (more motifs, Jaspar2020 or HOCOMOCO)
  • Calling peaks on clusters ()

–>

–> –> –> –> –> –> –> –>

–> –> –> –> –> –> –>

–> –> –> –> –>

–> –> –> –>

–> –> –>


Ci0tLQp0aXRsZTogIlBzZXVkb3RpbWUgYW5hbHlzaXMgb2YgVC1jZWxscyBpbiBkZXZlbG9waW5nIHRoeW11cyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGNvbm9zKQpsaWJyYXJ5KGdncHVicikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoU2luZ2xlQ2VsbEV4cGVyaW1lbnQpCmxpYnJhcnkoY2hyb21WQVIpCmxpYnJhcnkobW90aWZtYXRjaHIpCmxpYnJhcnkoQlNnZW5vbWUuSHNhcGllbnMuVUNTQy5oZzM4KQojIGxpYnJhcnkobW9ub2NsZTMpCnNvdXJjZSgifi9tdWx0aU9taWNfYmVuY2htYXJrL3V0aWxzLlIiKQpzb3VyY2UoIn4vbXVsdGlPbWljX2JlbmNobWFyay9pbnRlZ3JhdGVCZW5jaG1hcmsuUiIpCnNvdXJjZSgifi9tdWx0aU9taWNfYmVuY2htYXJrL3ByZXByb2Nlc3Mvc2VsZWN0RmVhdHVyZXMuUiIpCgojIyBNYWtlIG91dHB1dCBkaXJlY3RvcnkKb3V0ZGlyIDwtICJ+L211bHRpT21pY19iZW5jaG1hcmsvcmVwb3J0L291dHB1dC8yMDE5MTEyN190Y2VsbFRyYWplY3RvcnkvIgppZmVsc2UoIWRpci5leGlzdHMob3V0ZGlyKSwgZGlyLmNyZWF0ZShvdXRkaXIpLCBGQUxTRSkKYGBgCiAKIApCYXNlZCBvbiB0aGUgcmVzdWx0cyBvZiBteSBiZW5jaG1hcmssIEkgc2V0IG91dCB0byBhbGlnbiBleHByZXNzaW9uIGFuZCBhY2Nlc3NpYmlsaXR5IHByb2ZpbGVzIGZyb20gdGhlIEY3NCBkZXZlbG9waW5nIHRoeW11cyBkYXRhc2V0IHRvIGRldGVjdCBjaGFuZ2VzIGluIGFjY2Vzc2liaWxpdHkgYWxvbmcgcHNldWRvdGltZSB0cmFqZWN0b3JpZXMuIFdoaWxlIHRoZSBiZW5jaG1hcmsgd2FzIGJhc2VkIG9uIHRoZSB0YXNrIG9mIGxhYmVsIHByb3BhZ2F0aW9uLCBJIGhlcmUgdXNlIHRoZSB0d28gbW9zdCBmYWl0aGZ1bCBtZXRob2RzIChTZXVyYXQgQ0NBIGFuZCBDb25vcykgdG8gYWNoaWV2ZSBhIGNvbW1vbiBlbWJlZGRpbmcgb2YgQVRBQy1zZXEgYW5kIFJOQS1zZXEgY2VsbHMuCgpMb2FkIGRhdGFzZXRzLgoKYGBge3J9CnJuYS5zY2UgPC0gcmVhZFJEUygifi9teV9kYXRhL0Y3NF9STkFfc2V1cmF0X3Byb2Nlc3NlZC5SRFMiKQphdGFjLnNjZSA8LSByZWFkUkRTKCJ+L215X2RhdGEvRjc0X0FUQUNfc25hcEF0YWNfcHJvY2Vzc2VkX2JnbWF0LlJEUyIpCgojIyBSZS1ub3JtYWxpemUgUk5BIGRhdGEKc2V1LnJuYSA8LSBhcy5TZXVyYXQocm5hLnNjZSwgY291bnRzID0gImNvdW50cyIpCnNldS5ybmEgPC0gTm9ybWFsaXplRGF0YShzZXUucm5hKQpsb2djb3VudHMocm5hLnNjZSkgPC0gc2V1LnJuYUBhc3NheXMkUk5BQGRhdGEKCmBgYAoKRmlsdGVyIGdlbmVzIHdpdGggemVybyB2YXJpYW5jZQpgYGB7cn0Kcm5hLmdlbmUudmFyIDwtIGFzLm1hdHJpeChjb3VudHMocm5hLnNjZSkpICU+JSByb3dWYXJzKCkKYXRhYy5nZW5lLnZhciA8LSBhcy5tYXRyaXgoY291bnRzKGF0YWMuc2NlKSkgJT4lIHJvd1ZhcnMoKQoKcm5hLnNjZSA8LSBybmEuc2NlW3doaWNoKHJuYS5nZW5lLnZhciA+IDApLF0KYXRhYy5zY2UgPC0gYXRhYy5zY2Vbd2hpY2goYXRhYy5nZW5lLnZhciA+IDApLF0KCnJuYS5zY2U7IGF0YWMuc2NlCmBgYAoKCiMjIEludGVncmF0aW9uIG9mIFQgY2VsbHMgY2x1c3RlcnMKSSByZS1ydW4gdGhlIGludGVncmF0aW9uIGJhc2VkIG9uIHRoZSBUIGNlbGwgc3Vic2V0LiBUbyBzZWxlY3QgY2VsbHMgZnJvbSB0aGUgc2NBVEFDIGRhdGFzZXQsIEkgdGFrZSB0aGUgU25hcEFUQUMgY2x1c3RlcnMgdGhhdCBiZXN0IGNvcnJlc3BvbmQgdG8gVC1jZWxscywgYmFzZWQgb24gbGFiZWwgdHJhbnNmZXIuCgpgYGB7cn0KdGNlbGxzLnNjZS5hdGFjIDwtIGF0YWMuc2NlWyx3aGljaChhcy5udW1lcmljKGF0YWMuc2NlJHNldXJhdF9jbHVzdGVycykgJWluJSBjKDE6OSkpXQoKdGNlbGxzLnJuYS5peCA8LSB3aGljaChybmEuc2NlJGFubm90YXRpb24gJWluJSBjKCJETiIsIkRQIChRKSIsICJEUCAoUCkiLCAiU1AgKDEpIiwgIlNQICgyKSIpKQp0Y2VsbHMuc2NlLnJuYSA8LSBybmEuc2NlWyx0Y2VsbHMucm5hLml4XQoKdGNlbGxzLnNjZS5saXN0IDwtIGxpc3QoUk5BPXRjZWxscy5zY2Uucm5hLCBBVEFDPXRjZWxscy5zY2UuYXRhYykKCiMjIE1ha2UgY29sb3IgcGFsZXR0ZSA0IGNlbGwgdHlwZXMKY2VsbC50eXBlcyA8LSBhcy5jaGFyYWN0ZXIodW5pcXVlKHRjZWxscy5zY2Uucm5hJGFubm90YXRpb24pKQpjZWxsLnR5cGUucGFsIDwtIGJyZXdlci5wYWwobGVuZ3RoKGNlbGwudHlwZXMpLCAiU2V0MSIpICU+JSByZXYoKSAlPiUgc2V0TmFtZXMoY2VsbC50eXBlcykKYGBgCgpOZXh0LCBJIHNlbGVjdCBnZW5lcyBvbiB3aGljaCB0byBwZXJmb3JtIGludGVncmF0aW9uLiBJIHRha2UgdGhlIHVuaW9uIG9mIHRoZSBtb3N0IHZhcmlhYmxlIGZlYXR1cmVzIGluIHRoZSBSTkEgZGF0YXNldCBhbmQgdGhlIG1vc3QgY292ZXJlZCBmZWF0dXJlcyBpbiB0aGUgQVRBQyBkYXRhc2V0CgpgYGB7cn0KaGNnLmF0YWMgPC0gc2VsZWN0X2hpZ2hseUNvdmVyZWQodGNlbGxzLnNjZS5saXN0JEFUQUMsIGZyYWNfY2VsbHMgPSAwLjIpCmh2Zy5ybmEgPC0gc2VsZWN0X2hpZ2hseVZhcmlhYmxlKHRjZWxscy5zY2UubGlzdCRSTkEpCgpzZXUucm5hIDwtIEZpbmRWYXJpYWJsZUZlYXR1cmVzKHNldS5ybmEsIG5mZWF0dXJlcyA9IDIwMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBzZWxlY3Rpb24ubWV0aG9kID0gIm12cCIsIGRpc3BlcnNpb24uY3V0b2ZmPWMoMC43LCAxMDApLCBtZWFuLmN1dG9mZj1jKDAuMDIsIDMpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQpodmcucm5hIDwtIFZhcmlhYmxlRmVhdHVyZXMoc2V1LnJuYSkKClZhcmlhYmxlRmVhdHVyZVBsb3Qoc2V1LnJuYSkKVXBTZXRSOjp1cHNldChVcFNldFI6OmZyb21MaXN0KGxpc3QoSFZHLlJOQT1odmcucm5hLCBIQ0cuQVRBQz1oY2cuYXRhYykpKQpgYGAKClJlbW92ZSBjZWxsIGN5Y2xlIGdlbmVzLCB0aGF0IG1pZ2h0IGludGVyZmVyZSB3aXRoIHBzZXVkb3RpbWUgb3JkZXJpbmcKYGBge3J9CmNlbGxfY3ljbGVfZ2VuZXMgPC0gcmVhZC50YWJsZSgifi9hbm5vdGF0aW9ucy9jZWxsX2N5Y2xlX2dlbmVzLnRzdiIpJFYxCgppbnRlZ3JhdGVfZmVhdHVyZXNfdW5pb24gPC0gdW5pb24oaHZnLnJuYSwgaGNnLmF0YWMpCmludGVncmF0ZV9mZWF0dXJlc191bmlvbiA8LSBzZXRkaWZmKGludGVncmF0ZV9mZWF0dXJlc191bmlvbiwgY2VsbF9jeWNsZV9nZW5lcykgCgojIyBTZWxlY3QgZmVhdHVyZXMgaW4gYm90aCBkYXRhc2V0cwppbnRlZ3JhdGVfZmVhdHVyZXNfdW5pb24gPC0gaW50ZXJzZWN0KGludGVncmF0ZV9mZWF0dXJlc191bmlvbiwgaW50ZXJzZWN0KHJvd25hbWVzKHRjZWxscy5zY2UubGlzdCRBVEFDKSwgcm93bmFtZXModGNlbGxzLnNjZS5saXN0JFJOQSkpKSAKCmBgYAoKIyMjIyBWaXN1YWxpemUgVCBjZWxscyBpbiBSTkEgZGF0YXNldApgYGB7cn0KdGNlbGxzLnNldS5saXN0IDwtIG1hcCh0Y2VsbHMuc2NlLmxpc3QsIH4gYXMuU2V1cmF0KC54KSkKdGNlbGxzLlJOQS51bmlvbiA8LSB0Y2VsbHMuc2V1Lmxpc3QkUk5BClZhcmlhYmxlRmVhdHVyZXModGNlbGxzLlJOQS51bmlvbikgPC0gaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uCnRjZWxscy5STkEudW5pb24gPC0gU2NhbGVEYXRhKHRjZWxscy5STkEudW5pb24pICU+JSBSdW5QQ0EoKSAlPiUgUnVuVU1BUChkaW1zPTE6NDApCgpEaW1QbG90KHRjZWxscy5STkEudW5pb24sIGdyb3VwLmJ5ID0gImFubm90YXRpb24iLCBsYWJlbD1UUlVFKSArIGdndGl0bGUoIlJOQSAtIGZlYXR1cmUgdW5pb24iKQpgYGAKClZpc3VhbGl6ZSBtYXJrZXJzIApgYGB7ciwgZmlnLndpZHRoPTE1LCBmaWcuaGVpZ2h0PTEwfQp0LmNlbGwubWFya2VycyA8LSBsaXN0KGtub3duLm1hcmtlcnMgPSBjKCJDRDM0IiwgIklHTEwxIiwgIlRSR0MyIiwgIlRSREMiLCAiUFRDUkEiLCAiVFJCQzIiLCAiVFJBQyIsICJDRDQiLCAiQ0Q4QSIsICJDRDhCIiksCiAgICAgICAgICAgICAgICAgICAgICAgY2hlbW9raW5lLnJlY2VwdG9ycyA9IGMoIkNDUjkiLCAiQ0NSNyIpLAogICAgICAgICAgICAgICAgICAgICAgIHRjci5hY3RpdmF0aW9uID0gYygiQ0Q1IiwgIkNEMjciKSwKICAgICAgICAgICAgICAgICAgICAgICBwcm9saWZlcmF0aW9uPWMoIlBDTkEiLCAiQ0RLMSIsICJNS0k2NyIpLAogICAgICAgICAgICAgICAgICAgICAgIGN5Y2xpbi5EID0gYygiQ0NORDIiLCAiQ0NORDMiKSwKICAgICAgICAgICAgICAgICAgICAgICByZWNvbWJpbmF0aW9uPWMoIlJBRzEiLCAiUkFHMiIpLAogICAgICAgICAgICAgICAgICAgICAgIGFwb3B0b3Npcz1jKCJIUksiLCJCTUYiLCAiVFA1M0lOUDEiKSwKICAgICAgICAgICAgICAgICAgICAgICBzdGFnZS5tYXJrZXJzID0gYygiU1QxOCIsICJISVZFUDMiLCAiUkdQRDMiLCAiU01QRDMiLCAiQVFQMyIsICJST1JDIiwgIlNBVEIxIiwgIlRPWDIiKQogICAgICAgICAgICAgICAgICAgICAgICkgCiMgRmVhdHVyZVBsb3QodGNlbGxzLlJOQS5yZWYsIGZlYXR1cmVzID0gdC5jZWxsLm1hcmtlcnMka25vd24ubWFya2VycywgY29scyA9IHZpcmlkaXM6OnZpcmlkaXMobj0xMCkpCkZlYXR1cmVQbG90KHRjZWxscy5STkEudW5pb24sIGZlYXR1cmVzID0gdC5jZWxsLm1hcmtlcnMka25vd24ubWFya2VycywgY29scyA9IHZpcmlkaXM6OnZpcmlkaXMobj0xMCkpCmBgYAoKIyMjIyBWaXN1YWxpemUgVCBjZWxscyBpbiBBVEFDIGRhdGFzZXQKCkNvbG9yZWQgYnkgY2x1c3RlcnMgY2FsbGVkIHdpdGggU25hcEFUQUMKCmBgYHtyfQp0Y2VsbHMuQVRBQy51bmlvbiA8LSB0Y2VsbHMuc2V1Lmxpc3QkQVRBQwojIHRjZWxscy5BVEFDLnVuaW9uIDwtIE5vcm1hbGl6ZURhdGEodGNlbGxzLkFUQUMudW5pb24pClZhcmlhYmxlRmVhdHVyZXModGNlbGxzLkFUQUMudW5pb24pIDwtIGludGVncmF0ZV9mZWF0dXJlc191bmlvbgp0Y2VsbHMuQVRBQy51bmlvbiA8LSBSdW5MU0kodGNlbGxzLkFUQUMudW5pb24sIG49NTAsIHNjYWxlLm1heCA9IE5VTEwpCnRjZWxscy5BVEFDLnVuaW9uIDwtIFJ1blVNQVAodGNlbGxzLkFUQUMudW5pb24sIHJlZHVjdGlvbiA9ICJsc2kiLCBkaW1zID0gMTo1MCkKCkRpbVBsb3QodGNlbGxzLkFUQUMudW5pb24sIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAic2V1cmF0X2NsdXN0ZXJzIiwgbGFiZWwgPSBUUlVFKSArIGdndGl0bGUoIkFUQUMgZ21hdCIpCmBgYAoKIyMjIyBSdW4gQ0NBIAoKTWFrZXMgaW1wdXRlZCB0cmFuc2NyaXB0b21lIHByb2ZpbGUgZm9yIHRoZSBBVEFDLXNlcSBjZWxscyB0byBhbGxvdyBjby1lbWJlZGRpbmcKCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NSwgZXZhbD1GQUxTRX0Kc2NlLmxpc3QgPC0gdGNlbGxzLnNjZS5saXN0CnJlZmVyZW5jZSA9ICJSTkEiCnF1ZXJ5ID0gIkFUQUMiIApzZXVyYXQubGlzdCA8LSBpbWFwKHNjZS5saXN0LCB+IGFzLlNldXJhdCgueCwgYXNzYXk9LnkpKQpzZXVyYXQubGlzdCA8LSBpbWFwKHNldXJhdC5saXN0LCB+IFJlbmFtZUNlbGxzKC54LCBhZGQuY2VsbC5pZD0ueSkpCiMjIFNjYWxlIGRhdGEKc2V1cmF0Lmxpc3QgPC0gbWFwKHNldXJhdC5saXN0LCB+IFNjYWxlRGF0YSgueCkpCiMjIENhbGN1bGF0ZSBDQ0EgYW5jaG9ycwp0cmFuc2Zlci5hbmNob3JzIDwtIEZpbmRUcmFuc2ZlckFuY2hvcnMocmVmZXJlbmNlID0gc2V1cmF0Lmxpc3RbW3JlZmVyZW5jZV1dLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1ZXJ5ID0gc2V1cmF0Lmxpc3RbW3F1ZXJ5XV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IGludGVncmF0ZV9mZWF0dXJlc191bmlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAiY2NhIikKCiMjIEltcHV0ZSBleHByZXNzaW9uIHByb2ZpbGVzIGZvciBBVEFDIGNlbGxzIChmb3IgYWxsIGdlbmVzLCBub3QganVzdCBpbnRlZ3JhdGlvbiBmZWF0dXJlcykKcmVmZGF0YSA8LSBHZXRBc3NheURhdGEoc2V1cmF0Lmxpc3QkUk5BLCBhc3NheSA9ICJSTkEiLCBzbG90ID0gImRhdGEiKQppbXB1dGF0aW9uIDwtIFRyYW5zZmVyRGF0YShhbmNob3JzZXQgPSB0cmFuc2Zlci5hbmNob3JzLCByZWZkYXRhID0gcmVmZGF0YSwgd2VpZ2h0LnJlZHVjdGlvbiA9IHNldXJhdC5saXN0JEFUQUNbWyJMU0kiXV0pCgojIyBNZXJnZSBkYXRhc2V0cyBhbmQgY28tZW1iZWQKc2V1cmF0Lmxpc3QkQVRBQ1tbIlJOQSJdXSA8LSBpbXB1dGF0aW9uCmNvZW1iZWQgPC0gbWVyZ2UoeCA9IHNldXJhdC5saXN0JFJOQSwgeSA9IHNldXJhdC5saXN0JEFUQUMpCgpjb2VtYmVkIDwtIFNjYWxlRGF0YShjb2VtYmVkLCBmZWF0dXJlcyA9IGludGVncmF0ZV9mZWF0dXJlc191bmlvbiwgZG8uc2NhbGUgPSBGQUxTRSkKY29lbWJlZCA8LSBSdW5QQ0EoY29lbWJlZCwgZmVhdHVyZXMgPSBpbnRlZ3JhdGVfZmVhdHVyZXNfdW5pb24sIHZlcmJvc2UgPSBGQUxTRSkKY29lbWJlZCA8LSBSdW5VTUFQKGNvZW1iZWQsIGRpbXMgPSAxOjMwKQoKY29lbWJlZCA8LSBBZGRNZXRhRGF0YShjb2VtYmVkLCBtZXRhZGF0YSA9IGlmZWxzZShjb2xuYW1lcyhjb2VtYmVkKSAlaW4lIGNvbG5hbWVzKHNldXJhdC5saXN0W1tyZWZlcmVuY2VdXSksIHJlZmVyZW5jZSwgcXVlcnkpLCBjb2wubmFtZSA9ICJ0ZWNoIikKYGBgCgoKYGBge3IsIGVjaG89RkFMU0V9CiMjIExvYWQgb3V0cHV0IGZvciBxdWljayBrbml0dGluZwpjb2VtYmVkIDwtIHJlYWRSRFMoIn4vbXlfZGF0YS9UY2VsbHNfQ0NBX2ludGVncmF0aW9uX3NldXJhdF8yMDE5MTIwMy5SbWQiKQpgYGAKCjwhLS0gUnVuIENvbm9zIC0tPgo8IS0tIGBgYHtyfSAtLT4KPCEtLSBkYXRhLnByb2Nlc3NlZCA8LSBtYXAoc2NlLmxpc3QsIH4gYXMuU2V1cmF0KC54KSkgIC0tPgo8IS0tIFZhcmlhYmxlRmVhdHVyZXMoZGF0YS5wcm9jZXNzZWRbW3JlZmVyZW5jZV1dKSA8LSBpbnRlZ3JhdGVfZmVhdHVyZXNfdW5pb24gLS0+CjwhLS0gVmFyaWFibGVGZWF0dXJlcyhkYXRhLnByb2Nlc3NlZFtbcXVlcnldXSkgPC0gaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uIC0tPgo8IS0tIGRhdGEucHJvY2Vzc2VkIDwtIG1hcChkYXRhLnByb2Nlc3NlZCwgfiBTY2FsZURhdGEoLngpICU+JSBSdW5QQ0EoZGltcz0xOjMwKSkgLS0+CjwhLS0gbC5jb24gPC0gQ29ub3MkbmV3KGRhdGEucHJvY2Vzc2VkLG4uY29yZXM9MzApIC0tPgo8IS0tIGwuY29uJGJ1aWxkR3JhcGgoaz0xNSxrLnNlbGY9NSxrLnNlbGYud2VpZ2g9MC4wMSxuY29tcHM9MzAsbi5vZGdlbmVzPTVlMyxzcGFjZT0nUENBJykgIC0tPgoKPCEtLSBsLmNvbiRmaW5kQ29tbXVuaXRpZXMocmVzb2x1dGlvbj0xLjUpIC0tPgo8IS0tIGwuY29uJGVtYmVkR3JhcGgoYWxwaGE9MS8yKSAtLT4KCjwhLS0gY29ub3Mub3V0IDwtIGNvbm9zLm1vZGVsJG1vZGVsIC0tPgo8IS0tIGwuY29uJHBsb3RHcmFwaChjb2xvci5ieSA9ICJzYW1wbGUiKSAtLT4KCjwhLS0gZ2VuZVggPC0gc2V1cmF0Lmxpc3RbW3JlZmVyZW5jZV1dQGFzc2F5cyRSTkFAc2NhbGUuZGF0YVszLF0gLS0+CjwhLS0gZ2VuZVggPC0gc2V0TmFtZXMoYW5ub3RhdGlvblssMV0sIHJvd25hbWVzKGFubm90YXRpb24pKSAtLT4KPCEtLSBuZXcubGFiZWwucHJvYmFiaWxpdGllcyA8LSBsLmNvbiRwcm9wYWdhdGVMYWJlbHMobGFiZWxzID0gZ2VuZVgsIHZlcmJvc2UgPSBULCBmaXhlZC5pbml0aWFsLmxhYmVscz1UKSAtLT4KPCEtLSBoaXN0KG5ldy5sYWJlbC5wcm9iYWJpbGl0aWVzKSAtLT4KPCEtLSBsLmNvbiRjb3JyZWN0R2VuZXMoZ2VuZXMgPSBpbnRlZ3JhdGVfZmVhdHVyZXNfdW5pb24sIGNvdW50Lm1hdHJpeCA9IE1hdHJpeChzZXVyYXQubGlzdCRBVEFDQGFzc2F5cyRBVEFDQGRhdGEpKSAtLT4KCjwhLS0gYGBgIC0tPgoKIyMjIyBUcmFuc2ZlciBsYWJlbHMgb24gQVRBQyBkYXRhc2V0CmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NSwgZXZhbD1GQUxTRX0KY2VsbHR5cGUucHJlZGljdGlvbnMgPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IHRyYW5zZmVyLmFuY2hvcnMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVmZGF0YSA9IHNldXJhdC5saXN0W1tyZWZlcmVuY2VdXSRhbm5vdGF0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdlaWdodC5yZWR1Y3Rpb24gPSBzZXVyYXQubGlzdCRBVEFDW1siTFNJIl1dKQoKY29lbWJlZCA8LSBBZGRNZXRhRGF0YShjb2VtYmVkLCBtZXRhZGF0YSA9IGNlbGx0eXBlLnByZWRpY3Rpb25zKQpjb2VtYmVkQG1ldGEuZGF0YSAlPD4lCiAgcm93bmFtZXNfdG9fY29sdW1uKCkgJT4lCiAgZHBseXI6Om11dGF0ZShhbm5vdGF0aW9uPWlmZWxzZShpcy5uYShwcmVkaWN0ZWQuaWQpICwgYW5ub3RhdGlvbiwgTkEpKSAlPiUKICBjb2x1bW5fdG9fcm93bmFtZXMoKQoKY29lbWJlZEBtZXRhLmRhdGEgPC0KICBjb2VtYmVkQG1ldGEuZGF0YSAlPiUKICByb3duYW1lc190b19jb2x1bW4oKSAlPiUKICBkcGx5cjo6bXV0YXRlKGFubm90YXRpb249aWZlbHNlKGlzLm5hKGFubm90YXRpb24pICYgcHJlZGljdGlvbi5zY29yZS5tYXggPiAwLjUsIHByZWRpY3RlZC5pZCwgYW5ub3RhdGlvbikpICU+JQogIGRwbHlyOjptdXRhdGUoYW5ub3RhdGlvbj1pZmVsc2UoYW5ub3RhdGlvbj09IlNQICgyKSIsIE5BLCBhbm5vdGF0aW9uKSkgJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKCkKYGBgCgpgYGB7cn0KQ29tYmluZVBsb3RzKAogIGxpc3QoRGltUGxvdChjb2VtYmVkLCBncm91cC5ieSA9IGMoInByZWRpY3RlZC5pZCIpLCBjb2xzID0gY2VsbC50eXBlLnBhbCkgKyBnZ3RpdGxlKCJwcmVkaWN0aW9uIiksCiAgRGltUGxvdChjb2VtYmVkLCBncm91cC5ieSA9IGMoImFubm90YXRpb24iKSwgY29scyA9IGNlbGwudHlwZS5wYWwpICsgZ2d0aXRsZSgiT3JpZ2luYWwgKyBwcmVkaWN0aW9uIikpLAogIGxlZ2VuZCA9ICJ0b3AiCiAgKQpgYGAKYGBge3J9CkZlYXR1cmVQbG90KGNvZW1iZWQsIGZlYXR1cmVzID0gInByZWRpY3Rpb24uc2NvcmUubWF4IiwgY2VsbHMgPSB3aGljaChjb2VtYmVkJHRlY2g9PSJBVEFDIikpICsgc2NhbGVfY29sb3JfdmlyaWRpc19jKCkKYGBgCgoKIyMjIFJ1biBQc2V1ZG90aW1lIGFuYWx5c2lzIAoKSWRlbnRpZnkgY2VsbCBvZiBvcmlnaW4gYW1vbmcgdGhlIEROIGNlbGxzIGJhc2VkIG9uIGV4cHJlc3Npb24gb2YgSUdMTDEgYW5kIENEMzQKCmBgYHtyLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTB9CkZlYXR1cmVQbG90KGNvZW1iZWQsIGZlYXR1cmVzID0gYygiSUdMTDEiLCAiQ0QzNCIpLCBzcGxpdC5ieSA9ICJ0ZWNoIiwgc2xvdCA9ICJkYXRhIiwgY29scyA9IHZpcmlkaXM6OnZpcmlkaXMobj0xMDApKQpgYGAKYGBge3IsIGV2YWw9RkFMU0V9CmNlbGwub28gPC0KICBjb2VtYmVkQG1ldGEuZGF0YSAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKCJjZWxsIikgJT4lCiAgbXV0YXRlKElHTEwxPWNvZW1iZWRAYXNzYXlzJFJOQUBjb3VudHNbIklHTEwxIixjZWxsXSkgJT4lCiAgc2VsZWN0KGNlbGwsIGFubm90YXRpb24sIElHTEwxKSAlPiUKICBhcnJhbmdlKC1JR0xMMSkgJT4lCiAgZmlsdGVyKGFubm90YXRpb249PSJETiIpICU+JQogIHRvcF9uKDEsIElHTEwxKSAlPiUKICBwdWxsKGNlbGwpCgpjb2VtYmVkQHJlZHVjdGlvbnMkdW1hcEBjZWxsLmVtYmVkZGluZ3MgJT4lCiAgYXMudGliYmxlKHJvd25hbWVzPSJjZWxsIikgJT4lCiAgbXV0YXRlKGNlbGwub28gPSBpZmVsc2UoY2VsbCAlaW4lIGNlbGwub28sIFQsIEYpKSAlPiUKICBnZ3Bsb3QoYWVzKFVNQVBfMSwgVU1BUF8yKSkgKwogIGdlb21fcG9pbnQoY29sb3I9ImdyZXk1MCIpICsKICBnZW9tX3BvaW50KGRhdGE9LiAlPiUgZmlsdGVyKGNlbGwub28pLGNvbG9yPSdyZWQnKSArCiAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGRhdGE9LiAlPiUgZmlsdGVyKGNlbGwub28pLCBhZXMobGFiZWw9ImNlbGwgb2Ygb3JpZ2luIiksIGNvbG9yPSdyZWQnKSArCiAgdGhlbWVfY293cGxvdCgpIAoKY29lbWJlZCA8LSBBZGRNZXRhRGF0YShjb2VtYmVkLCBpZmVsc2UoY29sbmFtZXMoY29lbWJlZCk9PWNlbGwub28sIFRSVUUsIEZBTFNFKSwgY29sLm5hbWUgPSAiaXJvb3RfY2VsbCIpCgogIApgYGAKCgpgYGB7ciwgZXZhbD1GQUxTRX0KbWVyZ2VkLnNjZSA8LSBTaW5nbGVDZWxsRXhwZXJpbWVudChsaXN0KGNvdW50cz1jb2VtYmVkQGFzc2F5cyRSTkFAY291bnRzLCBsb2djb3VudHM9Y29lbWJlZEBhc3NheXMkUk5BQGRhdGEpLCBjb2xEYXRhPWNvZW1iZWRAbWV0YS5kYXRhWywgYygiYW5ub3RhdGlvbiIsICJ0ZWNoIiwgImlyb290X2NlbGwiKV0sCiAgICAgICAgICAgICAgICAgICAgIHJlZHVjZWREaW1zID0gbWFwKGNvZW1iZWRAcmVkdWN0aW9ucywgfiAueEBjZWxsLmVtYmVkZGluZ3MpKQoKc2F2ZVJEUyhvYmplY3QgPSBtZXJnZWQuc2NlLCAifi9teV9kYXRhL1RjZWxsc19DQ0FfaW50ZWdyYXRpb25fMjAxOTEyMDMuUkRTIikKc2F2ZVJEUyhvYmplY3QgPSBpbnRlZ3JhdGVfZmVhdHVyZXNfdW5pb24sICJ+L215X2RhdGEvaW50RmVhdHVyZXNfVGNlbGxzX0NDQV9pbnRlZ3JhdGlvbl8yMDE5MTIwMy5SRFMiKQpgYGAKCkkgaW5mZXIgcHNldWRvdGltZSB1c2luZyB0aGUgZGlmZnVzaW9uIHBzZXVkb3RpbWUgYWxnb3JpdGhtIGFzIGltcGxlbWVudGVkIGluIHNjYW5weS4gTWFraW5nIGFuIFIvcmV0aWN1bGF0ZSB3cmFwcGVyIGZvciB0aGlzIGZ1bmN0aW9uIHdvdWxkIGJlIG5pY2UsIGJ1dCBmb3Igbm93LCBzZWUgYG11bHRpT21pY19iZW5jaG1hcmsvRFBUX3RjZWxscy5pcHluYmAuCgpSZWFkIHNjYW5weSBvdXRwdXQgYW5kIHNhdmUgaW4gUiBvYmplY3QuCmBgYHtyLCBldmFsPUZBTFNFfQpkcHQgPC0gcmVhZC5jc3YoJ34vbXlfZGF0YS9UY2VsbHNfQ0NBX2ludGVncmF0aW9uXzIwMTkxMTI3X3NjYW5weV9kcHQuY3N2JykgJT4lCiAgc2VsZWN0KFgsIGRwdF9wc2V1ZG90aW1lKQoKY29lbWJlZCA8LSBBZGRNZXRhRGF0YShjb2VtYmVkLCBjb2x1bW5fdG9fcm93bmFtZXMoZHB0LCAnWCcpKQpzYXZlUkRTKGNvZW1iZWQsICJ+L215X2RhdGEvVGNlbGxzX0NDQV9pbnRlZ3JhdGlvbl9zZXVyYXRfMjAxOTEyMDMuUm1kIikKY29lbWJlZCA8LSByZWFkUkRTKCJ+L215X2RhdGEvVGNlbGxzX0NDQV9pbnRlZ3JhdGlvbl9zZXVyYXRfMjAxOTEyMDMuUm1kIikKYGBgCgoKVmlzdWFsaXplIHBzZXVkb3RpbWUKCmBgYHtyLCBmaWcud2lkdGg9MTB9CkZlYXR1cmVQbG90KGNvZW1iZWQsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZmVhdHVyZSA9ICJkcHRfcHNldWRvdGltZSIsIHNwbGl0LmJ5ID0gInRlY2giLCBjb2w9dmlyaWRpczo6dmlyaWRpcygxMCkpIApgYGAKClNhdmUgZmlndXJlCmBgYHtyLCBmaWcud2lkdGg9MTB9CmNvZW1iZWQudW1hcHMucGwgPC0gcGxvdF9ncmlkKAogIERpbVBsb3QoY29lbWJlZCwgZ3JvdXAuYnkgPSBjKCJ0ZWNoIikpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpLAogIERpbVBsb3QoY29lbWJlZCwgZ3JvdXAuYnkgPSBjKCJhbm5vdGF0aW9uIiksIGNvbHMgPSBjZWxsLnR5cGUucGFsLCBsYWJlbCA9IFRSVUUsIGxhYmVsLnNpemUgPSA1KSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksCiAgRmVhdHVyZVBsb3QoY29lbWJlZCwgcmVkdWN0aW9uID0gInVtYXAiLCBmZWF0dXJlID0gImRwdF9wc2V1ZG90aW1lIikgKyBzY2FsZV9jb2xvcl92aXJpZGlzX2MobmFtZT0iRGlmZnVzaW9uXG5wc2V1ZG90aW1lIikgKyBnZ3RpdGxlKCIiKSwKICBucm93PTEsIG5jb2w9MywgcmVsX3dpZHRocyA9IGMoMSwxLDEuMiksCiAgbGFiZWxzID0gYygiQSIsICJCIiwgIkMiKQopIAoKY29lbWJlZC51bWFwcy5wbCArCiAgZ2dzYXZlKHBhc3RlMChvdXRkaXIsICJjb2VtYmVkX3VtYXBzLnBuZyIpLCB3aWR0aD0xMiwgaGVpZ2h0ID0gNCkKYGBgCgoKCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMH0KY29lbWJlZEBtZXRhLmRhdGEgJT4lCiAgZHBseXI6Om11dGF0ZShgRFBUIHJhbmtgPWRlbnNlX3JhbmsoZHB0X3BzZXVkb3RpbWUpKSAlPiUKICBnZ3Bsb3QoYWVzKGBEUFQgcmFua2ApKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKGZpbGw9YW5ub3RhdGlvbiksIGJpbnM9NTApICsKICBmYWNldF9ncmlkKGFubm90YXRpb25+dGVjaCwgc2NhbGVzPSJmcmVlX3kiKSArCiAgdGhlbWVfYncoYmFzZV9zaXplID0gMTYpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjZWxsLnR5cGUucGFsKQoKYGBgCgpDaGVjayBleHByZXNzaW9uIG9mIG1hcmtlcnMgYWxvbmcgcHNldWRvdGltZQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTE0LCBmaWcuaGVpZ2h0PTR9CmNvZW1iZWRAYXNzYXlzJFJOQUBkYXRhW3QuY2VsbC5tYXJrZXJzJGtub3duLm1hcmtlcnMsIF0gJT4lCiAgYXMubWF0cml4KCkgJT4lCiAgcmVzaGFwZTI6Om1lbHQodmFybmFtZXM9YygiZ2VuZSIsICJjZWxsIikpICU+JQogIGxlZnRfam9pbihjb2VtYmVkQG1ldGEuZGF0YVssImRwdF9wc2V1ZG90aW1lIiwgZHJvcD1GXSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJjZWxsIikpICU+JQogIG11dGF0ZShwc2V1ZG90aW1lLnJhbms9ZGVuc2VfcmFuayhkcHRfcHNldWRvdGltZSkpICU+JQogIGdyb3VwX2J5KGdlbmUpICU+JQogIGFycmFuZ2UocHNldWRvdGltZS5yYW5rKSAlPiUKICAjIG11dGF0ZSh2YWx1ZT0odmFsdWUtbWluKHZhbHVlKSkvbWF4KHZhbHVlKS1taW4odmFsdWUpKSAlPiUKICBtdXRhdGUodmFsdWU9em9vOjpyb2xsbWVhbih2YWx1ZSwgaz01LCBmaWxsPU5BKSkgJT4lIAogICMgbXV0YXRlKHZhbHVlPSh2YWx1ZS1tZWFuKHZhbHVlKSkvc2QodmFsdWUpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKGdlbmU9ZmFjdG9yKGdlbmUsIGxldmVscz1yZXYodW5pcXVlKGdlbmUpKSkpICU+JQogIGdncGxvdChhZXMocHNldWRvdGltZS5yYW5rLCBnZW5lLCBmaWxsPXZhbHVlKSkgKwogIGdlb21fdGlsZSgpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhuYW1lPSJsb2cgZXhwcmVzc2lvbiIpICsKICB0aGVtZV9idyhiYXNlX3NpemUgPSAxNikgKwogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgpDaGVjayBhY2Nlc3NpYmlsaXR5IG9mIG1hcmtlcnMgYWxvbmcgcHNldWRvdGltZQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTE0LCBmaWcuaGVpZ2h0PTR9CmxvZ2NvdW50cyh0Y2VsbHMuc2NlLmF0YWMpW2ModC5jZWxsLm1hcmtlcnMkcmVjb21iaW5hdGlvblsxXSwgIlBUQ1JBIiksIF0gJT4lCiAgYXMubWF0cml4KCkgJT4lCiAgcmVzaGFwZTI6Om1lbHQodmFybmFtZXM9YygiZ2VuZSIsICJjZWxsIikpICU+JQogIG11dGF0ZShjZWxsPXN0cl9jKCJBVEFDXyIsIGNlbGwpKSAlPiUKICBsZWZ0X2pvaW4oY29lbWJlZEBtZXRhLmRhdGFbLCJkcHRfcHNldWRvdGltZSIsIGRyb3A9Rl0gJT4lIHJvd25hbWVzX3RvX2NvbHVtbigiY2VsbCIpKSAlPiUKICBtdXRhdGUocHNldWRvdGltZS5yYW5rPWRlbnNlX3JhbmsoZHB0X3BzZXVkb3RpbWUpKSAlPiUKICBncm91cF9ieShnZW5lKSAlPiUKICBhcnJhbmdlKHBzZXVkb3RpbWUucmFuaykgJT4lCiAgIyBtdXRhdGUodmFsdWU9KHZhbHVlLW1pbih2YWx1ZSkpL21heCh2YWx1ZSktbWluKHZhbHVlKSkgJT4lCiAgbXV0YXRlKHZhbHVlPXpvbzo6cm9sbG1lYW4odmFsdWUsIGs9MzAsIGZpbGw9TkEpKSAlPiUgCiAgIyBtdXRhdGUodmFsdWU9KHZhbHVlLW1lYW4odmFsdWUpKS9zZCh2YWx1ZSkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUoZ2VuZT1mYWN0b3IoZ2VuZSwgbGV2ZWxzPXJldih1bmlxdWUoZ2VuZSkpKSkgJT4lCiAgZ2dwbG90KGFlcyhwc2V1ZG90aW1lLnJhbmssIHZhbHVlLCBjb2xvcj1nZW5lKSkgKwogIGdlb21fbGluZShzaXplPTEpICsKICBmYWNldF9ncmlkKGdlbmV+LikKICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhuYW1lPSJhY2Nlc3NpYmlsaXR5IikgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE2KSArCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpKQpgYGAKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD00fQpEZWZhdWx0QXNzYXkoY29lbWJlZCkgPC0gIkFUQUMiCkZlYXR1cmVQbG90KGNvZW1iZWQsIGZlYXR1cmU9YygiUkFHMSIpLCBzbG90ID0gImRhdGEiLCBzcGxpdC5ieSA9ICJ0ZWNoIikKRmVhdHVyZVBsb3QoY29lbWJlZCwgZmVhdHVyZT1jKCJDRDQiKSwgc2xvdCA9ICJkYXRhIiwgc3BsaXQuYnkgPSAidGVjaCIpCkZlYXR1cmVQbG90KGNvZW1iZWQsIGZlYXR1cmU9YygiSUdMTDEiKSwgc2xvdCA9ICJkYXRhIiwgc3BsaXQuYnkgPSAidGVjaCIpCkZlYXR1cmVQbG90KGNvZW1iZWQsIGZlYXR1cmU9YygiQ0QzNCIpLCBzbG90ID0gImRhdGEiLCBzcGxpdC5ieSA9ICJ0ZWNoIikKYGBgCgpCaW4gcHNldWRvdGltZSBhbmQgdmlzdWFsaXplIGNlbGwgdHlwZSBjb21wb3NpdGlvbgoKYGBge3IsIGZpZy53aWR0aD0xNSwgZmlnLmhlaWdodD00fQpkcHQuZGYgPC0gCiAgY29lbWJlZEBtZXRhLmRhdGEgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKCJjZWxsIikgJT4lCiAgZHBseXI6Om11dGF0ZShkcHRfcmFuaz1kZW5zZV9yYW5rKGRwdF9wc2V1ZG90aW1lKSkgJT4lCiAgbXV0YXRlKGRwdF9iaW49Y3V0KGRwdF9yYW5rLCBicmVha3MgPSAxMDApKSAlPiUKICBtdXRhdGUoZHB0X2Jpbj1hcy5udW1lcmljKGRwdF9iaW4pKSAlPiUKICBzZWxlY3QoY2VsbCx0ZWNoLCBhbm5vdGF0aW9uLCBwcmVkaWN0aW9uLnNjb3JlLm1heCwgZHB0X2JpbiwgZHB0X3BzZXVkb3RpbWUsIGRwdF9yYW5rKQoKY2VsbC50eXBlLnBsIDwtIGRwdC5kZiAlPiUKICBnZ3Bsb3QoYWVzKGRwdF9iaW4sIGZpbGwgPSBhbm5vdGF0aW9uKSkgKwogICMgZ2VvbV9oaXN0b2dyYW0oYmlucz0xMDApICsKICBnZW9tX2JhcigpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Y2VsbC50eXBlLnBhbCwgbmEudmFsdWU9ImdyZXk1MCIpICsKICBmYWNldF9ncmlkKHRlY2h+Liwgc2NhbGVzPSJmcmVlX3kiKSArCiAgeGxhYigiUHNldWRvdGltZSBiaW4iKSArCiAgdGhlbWVfYncoYmFzZV9zaXplID0gMTYpCgpjZWxsLnR5cGUucGwKYGBgCgpDb3JyZWxhdGlvbiBiZXR3ZWVuIGdsb2JhbCBhY2Nlc3NpYmlsaXR5IGFuZCBwc2V1ZG90aW1lIG9yZGVyaW5nLgoKYGBge3IsIGZpZy53aWR0aD0xNSwgZmlnLmhlaWdodD04fQpzbmFwLm91dCA8LSByZWFkUkRTKGZpbGUgPSAifi9teV9kYXRhL2NlbGxyYW5nZXItYXRhYzExMF9jb3VudF8zMDQzOV9XU1NTODAzODM2MF9HUkNoMzgtMV8xXzAuc25hcEFUQUMuUkRTIikKCmdyb3VwcyA8LSBkcHQuZGZbZHB0LmRmJHRlY2g9PSJBVEFDIiwgYygiY2VsbCIsICJkcHRfYmluIiwgImFubm90YXRpb24iKV0KYm1hdCA8LSBzbmFwLm91dEBibWF0W3N0cl9yZW1vdmUoZ3JvdXBzJGNlbGwsICJBVEFDXyIpLF0KZnJhYy5hY2Nlc3NpYmxlIDwtIHJvd1N1bXMoYm1hdCkvbmNvbChibWF0KQphY2MuZnJhY3Rpb24ucGwgPC0gZ3JvdXBzICU+JQogIG11dGF0ZShmcmFjX2FjY2Vzc2libGU9ZnJhYy5hY2Nlc3NpYmxlW3N0cl9yZW1vdmUoY2VsbCwgIkFUQUNfIildKSAlPiUKICBnZ3Bsb3QoYWVzKGRwdF9iaW4sIGZyYWNfYWNjZXNzaWJsZSkpICsKICBnZW9tX2JveHBsb3QoYWVzKGdyb3VwPWFzLmZhY3RvcihkcHRfYmluKSksIG91dGxpZXIuYWxwaGEgPSAwLjMsIG91dGxpZXIuc2l6ZSA9IDAuNykgKwogICMgZ2VvbV9qaXR0ZXIoYWxwaGE9MC4xKSArCiAgeGxhYigiUHNldWRvdGltZSBiaW4iKSArCiAgeWxhYigiRnJhY3Rpb24gb2YgYWNjZXNzaWJsZSBiaW5zIikgKwogIGZhY2V0X2dyaWQoJ0FUQUMnfi4pICsKICB0aGVtZV9idyhiYXNlX3NpemUgPSAxNikgCiAgCmRwdC5wbCA8LSBwbG90X2dyaWQoY2VsbC50eXBlLnBsICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJ0b3AiLCBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCkpLCAKICAgICAgICAgIGFjYy5mcmFjdGlvbi5wbCwgCiAgICAgICAgICBhbGlnbiA9ICJ2IiwgbmNvbD0xLCBucm93PTIsIGF4aXM9ImwiKQoKZHB0LnBsICsKICBnZ3NhdmUocGFzdGUwKG91dGRpciwgIkRQVF9iaW5zLnBuZyIpLCB3aWR0aD0xMCwgaGVpZ2h0ID0gNykKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQpncm91cHMgJT4lCiAgbXV0YXRlKGZyYWNfYWNjZXNzaWJsZT1mcmFjLmFjY2Vzc2libGVbc3RyX3JlbW92ZShjZWxsLCAiQVRBQ18iKV0pICU+JQogIGdncGxvdChhZXMoZHB0X2JpbiwgZnJhY19hY2Nlc3NpYmxlKSkgKwogIGdlb21fYm94cGxvdChhZXMoZ3JvdXA9YXMuZmFjdG9yKGRwdF9iaW4pKSwgb3V0bGllci5hbHBoYSA9IDAuMywgb3V0bGllci5zaXplID0gMC43KSArCiAgIyBnZW9tX2ppdHRlcihhbHBoYT0wLjEpICsKICB4bGFiKCJQc2V1ZG90aW1lIGJpbiIpICsKICB5bGFiKCJGcmFjdGlvbiBvZiBhY2Nlc3NpYmxlIGJpbnMiKSArCiAgZmFjZXRfZ3JpZChhbm5vdGF0aW9ufi4pICsKICB0aGVtZV9idyhiYXNlX3NpemUgPSAxNikKYGBgCgpgYGB7ciwgZmlnLmhlaWdodD0xMH0KY29lbWJlZC51bWFwcy5wbCA8LSBwbG90X2dyaWQoCiAgRGltUGxvdChjb2VtYmVkLCBncm91cC5ieSA9IGMoInRlY2giKSkgKyB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDE2KSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKSAsCiAgRGltUGxvdChjb2VtYmVkLCBncm91cC5ieSA9IGMoImFubm90YXRpb24iKSwgY29scyA9IGNlbGwudHlwZS5wYWwsIGxhYmVsID0gVFJVRSwgbGFiZWwuc2l6ZSA9IDUpICsgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxNikgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLAogIEZlYXR1cmVQbG90KGNvZW1iZWQsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZmVhdHVyZSA9ICJkcHRfcHNldWRvdGltZSIpICsKICAgIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTYpICsKICAgIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYyhuYW1lPSJEaWZmdXNpb24gcHNldWRvdGltZSIpICsgZ2d0aXRsZSgiIikgKyAKICAgIGd1aWRlcyhjb2xvcj1ndWlkZV9jb2xvcmJhcihiYXJ3aWR0aCA9IDEwLHRpdGxlLnBvc2l0aW9uID0gInRvcCIpKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIiwgbGVnZW5kLmp1c3RpZmljYXRpb24gPSAiY2VudGVyIikgLAogIG5yb3c9MywgbmNvbD0xLCByZWxfaGVpZ2h0cyA9IGMoMS4yLDEsMS41KQopIAoKcGxvdF9ncmlkKGNvZW1iZWQudW1hcHMucGwsIGRwdC5wbCwgcmVsX3dpZHRocz1jKDEsMS45KSkgKwogIGdnc2F2ZShwYXN0ZTAob3V0ZGlyLCAidGNlbGxzX2ZpZ3VyZS5wbmciKSwgd2lkdGg9MTQsIGhlaWdodCA9IDEwKQpgYGAKCjwhLS0gVml6IG1hcmtlcnMgLS0+CjwhLS0gYGBge3J9IC0tPgo8IS0tIGFjYy5tYXQgPC0gY29lbWJlZEBhc3NheXMkQVRBQ0BkYXRhIC0tPgo8IS0tIG1hcmtlcnMuYWNjIDwtIGFjYy5tYXRbaW50ZXJzZWN0KGModC5jZWxsLm1hcmtlcnMka25vd24ubWFya2VycywgdC5jZWxsLm1hcmtlcnMkY2hlbW9raW5lLnJlY2VwdG9ycywgdC5jZWxsLm1hcmtlcnMkcmVjb21iaW5hdGlvbiksIHJvd25hbWVzKGFjYy5tYXQpKSwsIGRyb3A9Rl0gLS0+Cgo8IS0tIG1hcmtlcnMuZGYgPC0gZGF0YS5mcmFtZSh0KGFzLm1hdHJpeChtYXJrZXJzLmFjY1ssZHB0LmRmJGNlbGxbZHB0LmRmJHRlY2g9PSJBVEFDIl1dKSkpICU+JSAtLT4KPCEtLSAgIHJvd25hbWVzX3RvX2NvbHVtbigiY2VsbCIpICU+JSAtLT4KPCEtLSAgIHBpdm90X2xvbmdlcihjb2xzID0gcm93bmFtZXMobWFya2Vycy5hY2MpLCBuYW1lc190byA9ICJtYXJrZXIuZ2VuZSIsIHZhbHVlc190byA9ICJhY2Nlc3NpYmlsaXR5IikgLS0+Cgo8IS0tIGFubm90YXRpb24uaG0gPC0gYXRhYy5kcHQuZGYgJT4lIC0tPgo8IS0tICAgZ3JvdXBfYnkoZHB0X2JpbiwgYW5ub3RhdGlvbikgJT4lIC0tPgo8IS0tICAgc3VtbWFyaXNlKG49bigpKSAlPiUgLS0+CjwhLS0gICBnZ3Bsb3QoYWVzKGRwdF9iaW4sIGFubm90YXRpb24pKSArIC0tPgo8IS0tICAgZ2VvbV90aWxlKGFlcyhhbHBoYT1uLCBmaWxsPWFubm90YXRpb24pKSAgKyAtLT4KPCEtLSAgIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTYpICsgLS0+CjwhLS0gICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Y2VsbC50eXBlLnBhbCwgbmEudmFsdWU9ImdyZXk1MCIpICsgLS0+CjwhLS0gICBndWlkZXMoZmlsbD0nbm9uZScsIGFscGhhPSdub25lJykgKyAtLT4KPCEtLSAgIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSkgLS0+Cgo8IS0tIG1hcmtlcnMuaG0gPC0gYXRhYy5kcHQuZGYgJT4lIC0tPgo8IS0tICAgZnVsbF9qb2luKG1hcmtlcnMuZGYpICU+JSAtLT4KPCEtLSAgIGdyb3VwX2J5KGRwdF9iaW4sIG1hcmtlci5nZW5lKSAlPiUgLS0+CjwhLS0gICBzdW1tYXJpc2UoZnJhY19hY2Nlc3NpYmxlPXN1bShhY2Nlc3NpYmlsaXR5KS9uKCkpICU+JSAtLT4KPCEtLSAgIHVuZ3JvdXAoKSAlPiUgLS0+CjwhLS0gICBtdXRhdGUobWFya2VyLmdlbmU9ZmFjdG9yKG1hcmtlci5nZW5lLCBsZXZlbHMgPSBjKCJDRDM0IiwgIklHTEwxIiwgIlRSR0MyIiwgIlRSREMiLCAiUFRDUkEiLCAiVFJCQzIiLCAiQ0NSOSIsIkNDUjciLCAiUkFHMSIsICJSQUcyIiwgIlRSQUMiLCAiQ0Q0IiwgIkNEOEEiLCAiQ0Q4QiIpKSkgJT4lIC0tPgo8IS0tICAgbXV0YXRlKG1hcmtlci5nZW5lPWZhY3RvcihtYXJrZXIuZ2VuZSwgbGV2ZWxzID0gcmV2KGxldmVscyhtYXJrZXIuZ2VuZSkpKSkgJT4lIC0tPgo8IS0tICAgZ3JvdXBfYnkobWFya2VyLmdlbmUpICU+JSAtLT4KPCEtLSAgIG11dGF0ZShmcmFjX2FjY2Vzc2libGU9KGZyYWNfYWNjZXNzaWJsZSAtIG1pbihmcmFjX2FjY2Vzc2libGUpKS9tYXgoZnJhY19hY2Nlc3NpYmxlKSAtIG1pbihmcmFjX2FjY2Vzc2libGUpKSAlPiUgLS0+CjwhLS0gICBnZ3Bsb3QoYWVzKGRwdF9iaW4sIG1hcmtlci5nZW5lLCBmaWxsPWZyYWNfYWNjZXNzaWJsZSkpICsgIC0tPgo8IS0tICAgZ2VvbV90aWxlKCkgKyAtLT4KPCEtLSAgIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG5hbWU9IkZyYWMuY2VsbHMiKSArIC0tPgo8IS0tICAgeGxhYigiUHNldWRvdGltZSBiaW4iKSArIC0tPgo8IS0tICAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxNikgKyAtLT4KPCEtLSAgIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpIC0tPgoKPCEtLSBsZWcgPC0gZ2V0X2xlZ2VuZChtYXJrZXJzLmhtKSAtLT4KPCEtLSBncjEgPC0gcGxvdF9ncmlkKGFubm90YXRpb24uaG0sIG1hcmtlcnMuaG0gKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLCBucm93PTIsIHJlbF9oZWlnaHRzID0gYygxLDIpLCBhbGlnbiA9ICJ2IikgLS0+CjwhLS0gZ3IyIDwtIHBsb3RfZ3JpZChnZ3Bsb3QoKSArICB0aGVtZV92b2lkKCksbGVnLCBucm93PTIsIHJlbF9oZWlnaHRzID0gYygxLDIpKSAtLT4KPCEtLSBwbG90X2dyaWQoZ3IxLCBncjIsIHJlbF93aWR0aHMgPSBjKDMsMSkpIC0tPgo8IS0tIGBgYCAtLT4KCiMjIE1vdGlmIGFuYWx5c2lzIAoKSSBpbml0aWFsbHkgd2FudGVkIHRvIGNhbGwgcGVha3MgZnJvbSBTbmFwQVRBQyBjbHVzdGVycywgdGhlbiBidWlsZCBhIGNlbGwgeCBwZWFrIG1hdHJpeCBvbiB0aG9zZSBkZXRlY3RlZCBwZWFrcywgYnV0IFNuYXBBVEFDL01BQ1MyIGRvbid0IHNlZW0gdG8gd29yay4gCgo8IS0tIC0tLS0gLS0+CjwhLS0gKipUaGlzIGRvZXNuJ3Qgc2VlbSB0byB3b3JrKiogLS0+CjwhLS0gQ2FsbCBwZWFrcyAgLS0+CjwhLS0gYGBge3J9IC0tPgo8IS0tICMjIENhbGwgcGVha3Mgb24gY2x1c3RlcnMgLS0+CjwhLS0gY2x1c3RlcnMuc2VsIDwtIHVuaXF1ZSh0Y2VsbHMuc2NlLmF0YWMkc2V1cmF0X2NsdXN0ZXJzKSAtLT4KPCEtLSBwZWFrcy5scyA9IG1jbGFwcGx5KHNlcShjbHVzdGVycy5zZWwpLCBmdW5jdGlvbihpKXsgLS0+CjwhLS0gICBwcmludChwYXN0ZSgiY2x1c3RlciIsIGNsdXN0ZXJzLnNlbFtpXSkpIC0tPgo8IS0tICAgcGVha3MgPSBydW5NQUNTKCAtLT4KPCEtLSAgICAgICBvYmo9c25hcC5vdXRbd2hpY2goc25hcC5vdXRAbWV0YURhdGEkYmFyY29kZSAlaW4lIGNvbG5hbWVzKHRjZWxscy5zY2UuYXRhYylbdGNlbGxzLnNjZS5hdGFjJHNldXJhdF9jbHVzdGVycz09Y2x1c3RlcnMuc2VsW2ldXSksXSwgIC0tPgo8IS0tICAgICAgIG91dHB1dC5wcmVmaXg9cGFzdGUwKCJUY2VsbHNfRjc0X2NsdXN0ZXIiLCBjbHVzdGVycy5zZWxbaV0pLCAtLT4KPCEtLSAgICAgICBwYXRoLnRvLnNuYXB0b29scz0iL29wdC9jb25kYS9iaW4vc25hcHRvb2xzIiwgLS0+CjwhLS0gICAgICAgcGF0aC50by5tYWNzPSIvb3B0L2NvbmRhL2Jpbi9tYWNzMiIsIC0tPgo8IS0tICAgICAgIGdzaXplPSJocyIsICMgbW0sIGhzLCBldGMgLS0+CjwhLS0gICAgICAgYnVmZmVyLnNpemU9NTAwLCAgLS0+CjwhLS0gICAgICAgbnVtLmNvcmVzPTMsIC0tPgo8IS0tICAgICAgIG1hY3Mub3B0aW9ucz0iLS1ub21vZGVsIC0tc2hpZnQgMTAwIC0tZXh0IDIwMCAtLXF2YWwgNWUtMiAtQiAtLVNQTVIiLCAtLT4KPCEtLSAgICAgICB0bXAuZm9sZGVyPXRlbXBkaXIoKSAtLT4KPCEtLSAgKSAtLT4KPCEtLSBwZWFrcyAtLT4KPCEtLSB9LCBtYy5jb3Jlcz01KSAtLT4KCjwhLS0gcGVha3MubmFtZXMgPSBsaXN0LmZpbGVzKCJ+L215X2RhdGEvVGNlbGxzX3BlYWtzLyIsIHBhdHRlcm49Im5hcnJvd1BlYWsiLCBmdWxsLm5hbWVzID0gVCkgLS0+CjwhLS0gcGVhay5nci5scyA9IGxhcHBseShwZWFrcy5uYW1lcywgZnVuY3Rpb24oeCl7IC0tPgo8IS0tICAgcGVhay5kZiA9IHJlYWQudGFibGUoeCkgLS0+CjwhLS0gICBHUmFuZ2VzKHN0cl9yZW1vdmVfYWxsKHBlYWsuZGZbLDFdLCAiYid8JyIpLCBJUmFuZ2VzKHBlYWsuZGZbLDJdLCBwZWFrLmRmWywzXSkpIC0tPgo8IS0tIH0pIC0tPgo8IS0tIHBlYWsuZ3IgPSByZWR1Y2UoUmVkdWNlKGMsIHBlYWsuZ3IubHMpKSAtLT4KCjwhLS0gIyMgTWFrZSBjZWxsIGJ5IHBlYWsgbWF0cml4IChub3QgcnVuIGhlcmUpIC0tPgo8IS0tIHBlYWtzLmRmID0gYXMuZGF0YS5mcmFtZShwZWFrLmdyKVssMTozXTsgLS0+CjwhLS0gd3JpdGUudGFibGUocGVha3MuZGYsZmlsZSA9ICJ+L215X2RhdGEvVGNlbGxzX3BlYWtzL3BlYWtzLmNvbWJpbmVkLmJlZCIsYXBwZW5kPUZBTFNFLCAtLT4KPCEtLSAJCXF1b3RlPSBGQUxTRSxzZXA9Ilx0IiwgZW9sID0gIlxuIiwgbmEgPSAiTkEiLCBkZWMgPSAiLiIsICAtLT4KPCEtLSAJCXJvdy5uYW1lcyA9IEZBTFNFLCBjb2wubmFtZXMgPSBGQUxTRSwgcW1ldGhvZCA9IGMoImVzY2FwZSIsICJkb3VibGUiKSwgLS0+CjwhLS0gCQlmaWxlRW5jb2RpbmcgPSAiIikgLS0+CjwhLS0gYGBgIC0tPgoKPCEtLSBNYWtpbmcgY29tbW9uIHBlYWsgcmVmZXJlbmNlIHdpdGggc25hcHRvb2xzLiBJbiB0ZXJtaW5hbCAtLT4KPCEtLSBgYGAgLS0+CjwhLS0gc25hcHRvb2xzIHNuYXAtYWRkLXBtYXQgLS1zbmFwLWZpbGUgfi9teV9kYXRhL2NlbGxyYW5nZXItYXRhYzExMF9jb3VudF8zMDQzOV9XU1NTODAzODM2MF9HUkNoMzgtMV8xXzAuc25hcCAtLXBlYWstZmlsZSBwZWFrcy5jb21iaW5lZC5iZWQgIC0tPgo8IS0tIGBgYCAtLT4KCjwhLS0gQWRkIHBtYXQgdG8gc25hcCBvYmplY3QgLS0+CjwhLS0gYGBge3J9IC0tPgo8IS0tIHNuYXAub3V0IDwtIGNyZWF0ZVBtYXQoc25hcC5vdXQsIHBlYWsuZ3IsIGRvLnBhciA9IFQsIG51bS5jb3JlcyA9IDEwKSAtLT4KPCEtLSBgYGAgLS0+CjwhLS0gLS0tIC0tPgoKQWx0ZXJuYXRpdmU6IGxvYWQgcGVhayBtYXRyaXggZnJvbSBjZWxscmFuZ2VyIGFuZCBhZGQgdG8gc25hcCBvYmplY3QKYGBge3IsIGV2YWw9RkFMU0V9CmZpbHQucGVha3MgPC0gUmVhZDEwWF9oNSgifi9teV9kYXRhL2ZpbHRlcmVkX3BlYWtfYmNfbWF0cml4Lmg1IikKcGVha3MubWF0IDwtIHN0cl9zcGxpdChyb3duYW1lcyhmaWx0LnBlYWtzKSwgcGF0dGVybiA9ICI6fC0iKSAlPiUgbWFwKHJiaW5kKSAlPiUgcHVycnI6OnJlZHVjZShyYmluZCkKcGVha3MuZ3IgPC0gR1JhbmdlcyhwZWFrcy5tYXRbLDFdLCBJUmFuZ2VzKGFzLm51bWVyaWMocGVha3MubWF0WywyXSksIGFzLm51bWVyaWMocGVha3MubWF0WywzXSkpKQpzbmFwLnBtYXQgPC0gY3JlYXRlU25hcEZyb21QbWF0KG1hdD10KGZpbHQucGVha3NbLHNuYXAub3V0QGJhcmNvZGVdKSwgYmFyY29kZXM9c25hcC5vdXRAYmFyY29kZSwgcGVha3M9cGVha3MuZ3IpCnNuYXAucG1hdApgYGAKCkNhbGN1bGF0aW5nIGRldmlhdGlvbnMgaW4gVEYgYWNjZXNzaWJpbGl0eSB1c2luZyBDaHJvbVZBUi4gVGhpcyBpcyBhIG1lYXN1cmUgb2YgaG93IG11Y2ggaXMgbW90aWYgYWNjZXNzaWJpbGl0eSBpbiBlYWNoIGNlbGwgaXMgZW5yaWNoZWQgY29tcGFyZWQgdG8gYWxsIHRoZSBjZWxscyBhbmQgZ2VuZXJhbCBjZWxsIGNvdmVyYWdlLiBXaGlsZSBTbmFwQVRBQyBoYXMgYW4gd3JhcHBlciBhcm91bmQgQ2hyb21WQVIgdGhhdCBvdXRwdXRzIHRoZSBkZXZpYXRpb24gbWF0cml4LCBJIGp1c3QgdGFrZSB0aGUgY29kZSBmcm9tIHRoYXQgZnVuY3Rpb24gYW5kIHJ1biBldmVyeSBzdGVwIHNlcGFyYXRlbHkgdG8ga2VlcCB0aGUgdXNlZnVsIG91dHB1dHMgYW5kIHN0YXRpc3RpY3Mgb2YgY2hyb21WQVIuCgpgYGB7ciwgZXZhbD1GQUxTRX0Kc25hcC5wbWF0ID0gbWFrZUJpbmFyeShzbmFwLnBtYXQsICJwbWF0IikKCm9iaiA9IHNuYXAucG1hdAppbnB1dC5tYXQ9InBtYXQiCm1pbi5jb3VudD0xMApzcGVjaWVzPSJIb21vIHNhcGllbnMiCmdlbm9tZT1CU2dlbm9tZS5Ic2FwaWVucy5VQ1NDLmhnMzgKCmRhdGEudXNlID0gb2JqQHBtYXQKcGVhay51c2UgPSBvYmpAcGVhawoKbmNlbGwgPSBucm93KGRhdGEudXNlKQoKaWR5ID0gd2hpY2goTWF0cml4Ojpjb2xTdW1zKGRhdGEudXNlKSA+PSBtaW4uY291bnQpCmRhdGEudXNlID0gZGF0YS51c2VbLGlkeSxkcm9wcGluZz1UUlVFXQoJCnBlYWsudXNlID0gcGVhay51c2VbaWR5XQoKcnNlIDwtIFN1bW1hcml6ZWRFeHBlcmltZW50KAoJCWFzc2F5cyA9IGxpc3QoY291bnRzID0gdChkYXRhLnVzZSkpLCAKCQkJCSByb3dSYW5nZXMgPSBwZWFrLnVzZSwgCgkJCQkgY29sRGF0YSA9IERhdGFGcmFtZShDZWxsX1R5cGU9MTpucm93KGRhdGEudXNlKSwgZGVwdGg9TWF0cml4Ojpyb3dTdW1zKGRhdGEudXNlKSkKCSk7CnJzZSA8LSBhZGRHQ0JpYXMocnNlLCBnZW5vbWUgPSBnZW5vbWUpOwptb3RpZnMgPC0gZ2V0SmFzcGFyTW90aWZzKGNvbGxlY3Rpb24gPSAiQ09SRSIsIHNwZWNpZXM9c3BlY2llcykKbW90aWZfbW0gPC0gbWF0Y2hNb3RpZnMobW90aWZzLCByc2UsIGdlbm9tZSA9IGdlbm9tZSk7CmRldiA8LSBjb21wdXRlRGV2aWF0aW9ucyhvYmplY3QgPSByc2UsIGFubm90YXRpb25zID0gbW90aWZfbW0pOwp2YXIgPC0gY29tcHV0ZVZhcmlhYmlsaXR5KGRldikKYGBgCgpTYXZlCmBgYHtyLCBldmFsPUZBTFNFfQpyb3dEYXRhKGRldikgJTw+JQogIGFzLnRpYmJsZShyb3duYW1lcz0ibW90aWYiKSAlPiUKICBmdWxsX2pvaW4odmFyKSAlPiUKICBjb2x1bW5fdG9fcm93bmFtZXMoJ21vdGlmJykgJT4lCiAgRGF0YUZyYW1lKCkKCnNhdmVSRFMoZGV2LCAifi9teV9kYXRhL1RjZWxsc19wZWFrcy9UY2VsbHNfY2hyb21WYXJPdXRwdXQuUkRTIikgIApgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQpkZXYgPC0gcmVhZFJEUygifi9teV9kYXRhL1RjZWxsc19wZWFrcy9UY2VsbHNfY2hyb21WYXJPdXRwdXQuUkRTIikgIApgYGAKCmBgYHtyfQp2YXIgJT4lCiAgbXV0YXRlKHNpZ25pZj1pZmVsc2UocF92YWx1ZV9hZGogPCAwLjAxLCAic2lnbmlmIiwgTkEpKSAlPiUKICBtdXRhdGUocmFuaz1yYW5rKC12YXJpYWJpbGl0eSkpICU+JQogIGdncGxvdChhZXMocmFuaywgdmFyaWFiaWxpdHksIGNvbG9yPXNpZ25pZikpICsKICBnZW9tX3BvaW50KCkgKwogIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChkYXRhPS4gJT4lIGZpbHRlcihyYW5rIDwgODAgJiByYW5rID4gNTApLCBhZXMobGFiZWw9bmFtZSkpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSA1MCkKYGBgCgoKVmlzdWFsaXplIGRldmlhdGlvbiBzY29yZXMgb2YgdGhlIG1vc3QgdmFyaWFibGUgbW90aWZzLCBvcmRlcmVkIGluIHBzZXVkb3RpbWUuCgpgYGB7ciwgZmlnLndpZHRoPTE4LCBmaWcuaGVpZ2h0PTEwfQpzYW1wbGVfZHB0X2JpbnMuZGYgPC0gZHB0LmRmICU+JQogIG11dGF0ZShjZWxsPXN0cl9yZW1vdmUoY2VsbCwgIl5BVEFDXyIpKSAlPiUKICBmaWx0ZXIodGVjaD09IkFUQUMiKSAlPiUKICBhcnJhbmdlKGRwdF9wc2V1ZG90aW1lKQoKbW90aWYudG9wdmFyIDwtIHZhciAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJtb3RpZiIpICU+JSB0b3Bfbig1MCx2YXJpYWJpbGl0eSkgJT4lIHB1bGwobW90aWYpCnRmLnRvcHZhciA8LSBtb3RpZi50b3B2YXIgJT4lIHN0cl9yZW1vdmUoIi4rXyIpICU+JSBzdHJfcmVtb3ZlKCJcXCguK3w6LisiKQptbWF0LnRvcHZhciA8LSBkZXZAYXNzYXlzJGRhdGEkelttb3RpZi50b3B2YXIsc2FtcGxlX2RwdF9iaW5zLmRmJGNlbGxdCgpyb3duYW1lcyhtbWF0LnRvcHZhcikgPC0gdGYudG9wdmFyCnNtb290aC5tbWF0IDwtIGFwcGx5KG1tYXQudG9wdmFyLCAxLCBmdW5jdGlvbih4KSB6b286OnJvbGxtZWFuKHgsIGs9MzApKSAlPiUgdCgpIAoKdGlmZihwYXN0ZTAob3V0ZGlyLCAiY2hyb21WQVJfbW90aWZfaGVhdG1hcC50aWZmIiksIHdpZHRoPTkwMCwgaGVpZ2h0ID0gOTAwKQojIHBkZihwYXN0ZTAob3V0ZGlyLCAiY2hyb21WQVJfbW90aWZfaGVhdG1hcC5wZGYiKSwgd2lkdGg9OSwgaGVpZ2h0ID0gMTApCnNtb290aC5tbWF0ICU+JQogICMgbW1hdC50b3B2YXJbLHNhbXBsZV9kcHRfYmlucy5kZiRjZWxsXSAlPiUKICBwaGVhdG1hcDo6cGhlYXRtYXAoc2hvd19jb2xuYW1lcyA9IEYsIGNsdXN0ZXJfY29scyA9IEYsIGNsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9ICJjb3JyZWxhdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sID0gc2FtcGxlX2RwdF9iaW5zLmRmWyxjKCJjZWxsIiwgImFubm90YXRpb24iLCAiZHB0X3BzZXVkb3RpbWUiKV0gJT4lIGNvbHVtbl90b19yb3duYW1lcygiY2VsbCIpLCAKICAgICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBsaXN0KGFubm90YXRpb249Y2VsbC50eXBlLnBhbCwgZHB0X3BzZXVkb3RpbWU9dmlyaWRpczo6dmlyaWRpcygxMDApKSwgZm9udHNpemUgPSAxOCwgZm9udHNpemVfcm93ID0gMTQsCiAgICAgICAgICAgICAgICAgICAgICMgY29sb3IgPSBjb2xvclJhbXBQYWxldHRlKHJldihicmV3ZXIucGFsKG4gPSA3LCBuYW1lID0iU3BlY3RyYWwiKSkpKDEwMCkpCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcz1zZXEoLTMsMywgbGVuZ3RoLm91dCA9IDEwMCksIGxlZ2VuZCA9IEYsIGFubm90YXRpb25fbGVnZW5kID0gRiwKICAgICAgICAgICAgICAgICAgICAgbGVnZW5kX2JyZWFrcyA9IGMoLTIsIC0xLCAwLCAxLCAyLCAzKSwgbGVnZW5kX2xhYmVscyA9IGMoIi0yIiwgIi0xIiwgIjAiLCAiMSIsIjIiLCAiRGV2aWF0aW9uXG4oei1zY29yZSkiKQogICkKZGV2Lm9mZigpCmBgYAoKYGBge3IsIGZpZy53aWR0aD0xMH0KZHB0Lm9yZGVyIDwtCiAgZHB0LmRmICU+JQogIGZpbHRlcih0ZWNoPT0iUk5BIikgJT4lCiAgYXJyYW5nZShkcHRfcHNldWRvdGltZSkgCgpjb2VtYmVkIDwtIFNjYWxlRGF0YShjb2VtYmVkLCBkby5zY2FsZT1UUlVFKQpnZXhtYXQudG9wdmFyIDwtIGNvZW1iZWRAYXNzYXlzJFJOQUBzY2FsZS5kYXRhW3RmLnRvcHZhclt3aGljaCh0Zi50b3B2YXIgJWluJSByb3duYW1lcyhjb2VtYmVkQGFzc2F5cyRSTkFAc2NhbGUuZGF0YSkpXSxkcHQub3JkZXIkY2VsbF0Kc21vb3RoLmdleG1hdCA8LSBhcHBseShnZXhtYXQudG9wdmFyLCAxLCBmdW5jdGlvbih4KSB6b286OnJvbGxtZWFuKHgsIGs9MzApKSAlPiUgdCgpIApzbW9vdGguZ2V4bWF0ICU+JQogICMgdCgpICU+JSBzY2FsZSgpICU+JSB0KCkgJT4lCiAgcGhlYXRtYXA6OnBoZWF0bWFwKHNob3dfY29sbmFtZXMgPSBGLCBjbHVzdGVyX3Jvd3MgPSBULCBjbHVzdGVyX2NvbHMgPSBGLCAKICAgICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBkcHQub3JkZXJbLGMoImNlbGwiLCAiYW5ub3RhdGlvbiIsICJkcHRfcHNldWRvdGltZSIpXSAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCJjZWxsIiksCiAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sb3JzID0gbGlzdChhbm5vdGF0aW9uPWNlbGwudHlwZS5wYWwsIGRwdF9wc2V1ZG90aW1lPXZpcmlkaXM6OnZpcmlkaXMoMTAwKSksIGZvbnRzaXplID0gMTgsIGZvbnRzaXplX3JvdyA9IDEyLAogICAgICAgICAgICAgICAgICAgICBicmVha3M9c2VxKC0yLDIsIGxlbmd0aC5vdXQgPSAxMDApCiAgKQpgYGAKCkNvbXBhcmUgbW90aWYgYWNjZXNzaWJpbGl0eSB0cmVuZCB3aXRoIGdlbmUgZXhwcmVzc2lvbiB0cmVuZCBhbG9uZyBwc2V1ZG90aW1lLiBJIGZpbmQgYm90aCBleGFtcGxlcyBvZiBjb3JyZWxhdGlvbiBiZXR3ZWVuIGFjY2Vzc2liaWxpdHkgYW5kIFRGIGV4cHJlc3Npb24gKGUuZy4gUlVOWDIsIEVMSzMpIGFuZCBhbnRpLWNvcnJlbGF0aW9uIChlLmcuIEpVTiwgRVRWNikuCgpgYGB7cn0KY291bnRzLnRvcHZhciA8LSBjb2VtYmVkQGFzc2F5cyRSTkFAZGF0YVt0Zi50b3B2YXJbd2hpY2godGYudG9wdmFyICVpbiUgcm93bmFtZXMoY29lbWJlZEBhc3NheXMkUk5BQGRhdGEpKV0sZHB0Lm9yZGVyJGNlbGxdCmdleC5kZiA8LSAKICByZXNoYXBlMjo6bWVsdChhcy5tYXRyaXgoY291bnRzLnRvcHZhciksIHZhcm5hbWVzPWMoImdlbmUiLCAiY2VsbCIpKSAlPiUgCiAgIyByb3dpZF90b19jb2x1bW4oImRwdF9vcmRlciIpICU+JQogIG11dGF0ZShkYXRhPSJHZW5lXG5leHByZXNzaW9uIikKbW1hdC5kZiA8LSByZXNoYXBlMjo6bWVsdChtbWF0LnRvcHZhciwgdmFybmFtZXM9YygiZ2VuZSIsICJjZWxsIikpICU+JQogICMgcm93aWRfdG9fY29sdW1uKCJkcHRfb3JkZXIiKSAlPiUKICBtdXRhdGUoY2VsbD1zdHJfYygiQVRBQ18iLCBjZWxsKSkgJT4lCiAgbXV0YXRlKGRhdGE9Ik1vdGlmXG5kZXZpYXRpb24iKQoKcGxvdC50ZnMgPC0gZnVuY3Rpb24ocGxvdC50ZnMpewogIGJpbmRfcm93cyhnZXguZGYsIG1tYXQuZGYpICU+JQogIGxlZnRfam9pbihkcHQuZGZbLCBjKCJjZWxsIiwgImRwdF9wc2V1ZG90aW1lIiwgImFubm90YXRpb24iKV0sIGJ5PSJjZWxsIikgJT4lCiAgbXV0YXRlKGRwdF9yYW5rPWRlbnNlX3JhbmsoZHB0X3BzZXVkb3RpbWUpKSAlPiUKICBkcm9wX25hKGRwdF9wc2V1ZG90aW1lKSAlPiUKICAgIG11dGF0ZShkYXRhPWZhY3RvcihkYXRhLCBsZXZlbHM9YygiR2VuZVxuZXhwcmVzc2lvbiIsICJzbW9vdGgiLCAiTW90aWZcbmRldmlhdGlvbiIpKSkgJT4lCiAgZmlsdGVyKGdlbmUgJWluJSBwbG90LnRmcykgJT4lCiAgZ2dwbG90KGFlcyhkcHRfcmFuaywgdmFsdWUsIGNvbG9yPWRhdGEpKSArCiAgIyBnZW9tX3BvaW50KGRhdGE9LiAlPiUgZmlsdGVyKGRhdGEhPSJzbW9vdGgiKSwgYWVzKGNvbG9yPWFubm90YXRpb24pLCBzaXplPTAuNywgYWxwaGE9MC4zKSArCiAgZ2VvbV9zbW9vdGgoIG1ldGhvZD0ibG9lc3MiLHNwYW49MC4yKSArCiAgZmFjZXRfZ3JpZChkYXRhfmdlbmUsIHNjYWxlcz0iZnJlZSIpICsKICB4bGFiKCJQc2V1ZG90aW1lIHJhbmsiKSArCiAgdGhlbWVfYncoYmFzZV9zaXplID0gMTYpIAp9Cgp0ZnMgPC0gYygiSlVOIiwgIkZPU0wyIiwgIkZPU0wxIiwgIkZPUyIpCgpwZGYocGFzdGUwKG91dGRpciwgIlRGX3Bsb3RzLnBkZiIpLCB3aWR0aCA9IDgsIGhlaWdodCA9IDUpCmZvciAodGYgaW4gdGYudG9wdmFyKSB7CiAgcHJpbnQocGxvdC50ZnModGYpKQp9CmRldi5vZmYoKQoKbWFwKGxpc3QoIkpVTiIsICJFTEszIiwgIlJVTlgyIiwgIlJFTCIsICJGT1MiLCAiRVRWNiIsICJUQ0YzIiksIH4gcGxvdC50ZnMoLngpICsgZ2dzYXZlKHBhc3RlMChvdXRkaXIsIHBhc3RlMCgnVEZfcGxvdF8nLC54LCIucG5nIikpLCB3aWR0aCA9IDgsIGhlaWdodD01KSkKYGBgCgpgYGB7ciwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTZ9CnRmcyA8LSBjKCJSVU5YMiIsICJFTEszIiwiSlVOIiwgIkVUVjYiKQpwbG90LnRmcyh0ZnMpCgp0Zi5saXN0IDwtIG1hcCh0ZnMsIH4gcGxvdC50ZnMoLngpKQp0Zi5saXN0IDwtIG1hcCh0Zi5saXN0LCB+IC54ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSkKdGYubGlzdCA8LSBtYXBfaWYodGYubGlzdCwgYyhUUlVFLCBUUlVFLCBUUlVFLCBGQUxTRSksIH4gLnggKyB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpICkpCgp0Zi5saXN0CnBsb3RfZ3JpZChwbG90bGlzdCA9IHRmLmxpc3QsIGFsaWduPSJ2IiwgYXhpcz0ibCIsIG5yb3c9NCwgbmNvbD0xLCByZWxfaGVpZ2h0cyA9IGMoMSwxLDEsMS4xNSkpICsKICBnZ3NhdmUocGFzdGUwKG91dGRpciwgIlRGX3BsX2ZpZy5wbmciKSwgaGVpZ2h0ID0gMTEsIHdpZHRoID0gNSkKYGBgCgpgYGB7cn0KcGxvdC50ZnMoIlRGQVA0IikKYGBgCgojIyMgV2F5cyB0byBvcHRpbWl6ZSB0aGlzIGFuYWx5c2lzIAoKLSBJbXByb3ZlZCBtb3RpZiBkYXRhYmFzZSAobW9yZSBtb3RpZnMsIEphc3BhcjIwMjAgb3IgSE9DT01PQ08pCi0gQ2FsbGluZyBwZWFrcyBvbiBjbHVzdGVycyAoKQoKCjwhLS0gYGBge3IsIGZpZy53aWR0aD0xNCwgZmlnLmhlaWdodD03fSAtLT4KPCEtLSBiaW5kX3Jvd3MoYWNjZXNzLmRmLCBnZW5leC5kZikgJT4lIC0tPgo8IS0tICAgZmlsdGVyKGdlbmUgJWluJSBjKCJTUEkxIiwgIlJVTlgyIiwiUlVOWDMiLCAnVENGN0wyJywgIkUyRjQiKSkgJT4lIC0tPgo8IS0tICAgICBmaWx0ZXIoIXN0cl9kZXRlY3QoY2VsbCwgIl5BVEFDXyIpIHwgdGVjaD09IkFUQUMiKSAlPiUgLS0+CjwhLS0gICAjIGdyb3VwX2J5KHRlY2gsIGdlbmUpICU+JSAtLT4KPCEtLSAgICMgbXV0YXRlKHZhbHVlPXNjYWxlKHZhbHVlKSkgJT4lIC0tPgo8IS0tICAgIyB1bmdyb3VwKCkgJT4lIC0tPgo8IS0tICAgIyBkcm9wX25hKCkgJT4lIC0tPgo8IS0tICMgZmlsdGVyKHRlY2g9PSJSTkEiKSAlPiUgLS0+CjwhLS0gICBkcm9wX25hKGRwdF9iaW4pICU+JSAtLT4KPCEtLSAgIGdncGxvdChhZXMoZHB0X2JpbiwgdmFsdWUpKSArIC0tPgo8IS0tICAgZ2VvbV9wb2ludChhZXMoY29sb3I9YW5ub3RhdGlvbiksIGFscGhhPTAuMikgKyAtLT4KPCEtLSAgIGZhY2V0X2dyaWQodGVjaH5nZW5lLCBzY2FsZXMgPSAiZnJlZV95IikgKyAtLT4KPCEtLSAgIGdlb21fc21vb3RoKCkgLS0+CgoKPCEtLSBgYGAgLS0+Cgo8IS0tIGBgYHtyLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9N30gLS0+CjwhLS0gYmluZF9yb3dzKGFjY2Vzcy5kZiwgZ2VuZXguZGYpICU+JSAtLT4KPCEtLSAgIGZpbHRlcihnZW5lICVpbiUgYygiRUxLMyIsICJKVU5CIiwgIkZPUyIpKSAlPiUgLS0+CjwhLS0gICBmaWx0ZXIoIXN0cl9kZXRlY3QoY2VsbCwgIl5BVEFDXyIpIHwgdGVjaD09IkFUQUMiKSAlPiUgLS0+CjwhLS0gICAjIGdyb3VwX2J5KHRlY2gsIGdlbmUpICU+JSAtLT4KPCEtLSAgICMgbXV0YXRlKHZhbHVlPXNjYWxlKHZhbHVlKSkgJT4lIC0tPgo8IS0tICAgIyB1bmdyb3VwKCkgJT4lIC0tPgo8IS0tICAgZHJvcF9uYShkcHRfYmluKSAlPiUgLS0+CjwhLS0gICBnZ3Bsb3QoYWVzKGRwdF9iaW4sIHZhbHVlKSkgKyAtLT4KPCEtLSAgIGdlb21fcG9pbnQoYWVzKGNvbG9yPWFubm90YXRpb24pLCBhbHBoYT0wLjIpICsgLS0+CjwhLS0gICBmYWNldF9ncmlkKHRlY2h+Z2VuZSwgc2NhbGVzID0gImZyZWVfeSIpICsgLS0+CjwhLS0gICBnZW9tX3Ntb290aCgpICsgLS0+CjwhLS0gICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWNlbGwudHlwZS5wYWwpIC0tPgoKCjwhLS0gYGBgIC0tPgoKPCEtLSAjIyBQc2V1ZG90aW1lIGxhZyBiZXR3ZWVuIERQKFEpIGluIGFjY2Vzc2liaWxpdHkgYW5kIGdlbmUgZXhwcmVzc2lvbiAtLT4KPCEtLSBUaGUgRFAgKFEpIGNsdXN0ZXIgaW4gdGhlIEFUQUMgY2VsbHMgaXMgc2NvcmVkIHdpdGggaGlnaCBjb25maWRlbmNlIC0tPgo8IS0tIGBgYHtyfSAtLT4KPCEtLSBkcHEuY29lbWJlZCA8LSBjb2VtYmVkWyx3aGljaChjb2VtYmVkJGFubm90YXRpb249PSJEUCAoUSkiKV0gLS0+Cgo8IS0tIEZlYXR1cmVQbG90KGNvZW1iZWQsIGZlYXR1cmU9InByZWRpY3Rpb24uc2NvcmUubWF4IikgLS0+CjwhLS0gRGltUGxvdChjb2VtYmVkLCBncm91cC5ieSA9ImFubm90YXRpb24iLCBzcGxpdC5ieSA9ICJ0ZWNoIikgLS0+Cgo8IS0tIGBgYCAtLT4KCjwhLS0gYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02fSAtLT4KPCEtLSBjY2Eub2JqIDwtIHRyYW5zZmVyLmFuY2hvcnNAb2JqZWN0Lmxpc3RbWzFdXSAtLT4KPCEtLSBuZXcubWV0YWRhdGEgPC0gY29lbWJlZEBtZXRhLmRhdGFbLGMoImFubm90YXRpb24iLCAidGVjaCIpLCBkcm9wPUZdICU+JSByb3duYW1lc190b19jb2x1bW4oKSAlPiUgLS0+CjwhLS0gICBtdXRhdGUocm93bmFtZT1pZmVsc2Uoc3RyX2RldGVjdChyb3duYW1lLCAiXlJOQSIpLCBzdHJfYyhyb3duYW1lLCAiX3JlZmVyZW5jZSIpLCBzdHJfYyhyb3duYW1lLCAiX3F1ZXJ5IikpKSAlPiUgLS0+CjwhLS0gICBjb2x1bW5fdG9fcm93bmFtZXMoKSAtLT4KPCEtLSBjY2Eub2JqIDwtIEFkZE1ldGFEYXRhKGNjYS5vYmosIG5ldy5tZXRhZGF0YSkgLS0+CjwhLS0gY2NhLm9iakBtZXRhLmRhdGEgLS0+CjwhLS0gRGltUGxvdChjY2Eub2JqLCBncm91cC5ieT1jKCJhbm5vdGF0aW9uIiwidGVjaCIpLCByZWR1Y3Rpb24gPSAiY2NhIiwgZGltcyA9IDE6MikgLS0+CjwhLS0gRGltUGxvdChjY2Eub2JqLCBncm91cC5ieT1jKCJhbm5vdGF0aW9uIiwidGVjaCIpLCByZWR1Y3Rpb24gPSAiY2NhIiwgZGltcyA9IDM6NCkgLS0+CjwhLS0gRGltUGxvdChjY2Eub2JqLCBncm91cC5ieT1jKCJhbm5vdGF0aW9uIiwidGVjaCIpLCByZWR1Y3Rpb24gPSAiY2NhIiwgZGltcyA9IDU6NikgLS0+CjwhLS0gYGBgIC0tPgoKPCEtLSBgYGB7ciwgZmlnLmhlaWdodD0xOCwgZmlnLndpZHRoPTE4fSAtLT4KPCEtLSB0b3AuY2MuZ2VuZXMgPC0gY2NhLm9iakByZWR1Y3Rpb25zJGNjYS5sMkBmZWF0dXJlLmxvYWRpbmdzICU+JSAgLS0+CjwhLS0gICByZXNoYXBlMjo6bWVsdCh2YXJuYW1lcz1jKCJnZW5lIiwgIkNDIikpICU+JSAtLT4KPCEtLSAgIGdyb3VwX2J5KENDKSAlPiUgLS0+CjwhLS0gICBtdXRhdGUocmFuaz1yYW5rKGFicyh2YWx1ZSkpKSAlPiUgLS0+CjwhLS0gICB1bmdyb3VwKCkgJT4lIC0tPgo8IS0tICAgZmlsdGVyKHJhbmsgPiAobWF4KHJhbmspLTEwKSkgJT4lIC0tPgo8IS0tICAgcHVsbChnZW5lKSAlPiUgLS0+CjwhLS0gICB1bmlxdWUoKSAtLT4KCjwhLS0gYXRhYy5tYXQgPC0gY29lbWJlZEBhc3NheXMkQVRBQ0BkYXRhIC0tPgo8IS0tIHJuYS5tYXQgPC0gY29lbWJlZEBhc3NheXMkUk5BQGRhdGEgLS0+Cgo8IS0tIGF0YWMubWF0W2dlbmUub2ksXSAlPiUgLS0+CjwhLS0gICB7Llssd2hpY2goYXBwbHkoLiwyLCBmdW5jdGlvbih4KSBzdW0oeCkhPTApKV19ICU+JSAtLT4KPCEtLSAgIHBoZWF0bWFwOjpwaGVhdG1hcChzaG93X2NvbG5hbWVzPUYsIGNsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9ICJjb3JyZWxhdGlvbiIsIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IGNvZW1iZWRAbWV0YS5kYXRhWywiYW5ub3RhdGlvbiIsIGRyb3A9Rl0gLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgKSAtLT4KPCEtLSBgYGAgLS0+CjwhLS0gYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMH0gLS0+CjwhLS0gZHBxLmNlbGxzIDwtIHJvd25hbWVzKG5ldy5tZXRhZGF0YVtuZXcubWV0YWRhdGEkYW5ub3RhdGlvbj09IkRQIChRKSIsXSkgLS0+CjwhLS0gZHBxLnF1ZXJ5Lml4IDwtIHdoaWNoKHRyYW5zZmVyLmFuY2hvcnNAcXVlcnkuY2VsbHMgJWluJSBkcHEuY2VsbHMpIC0tPgo8IS0tIGRwcS5yZWYuaXggPC0gd2hpY2godHJhbnNmZXIuYW5jaG9yc0ByZWZlcmVuY2UuY2VsbHMgJWluJSBkcHEuY2VsbHMpIC0tPgo8IS0tIG5ldy5tZXRhZGF0YSAlPiUgLS0+CjwhLS0gICByb3duYW1lc190b19jb2x1bW4oKSAlPiUgLS0+CjwhLS0gICBmaWx0ZXIodGVjaD09IkFUQUMiKSAtLT4KPCEtLSB0cmFuc2Zlci5hbmNob3JzQGFuY2hvcnMgJT4lIC0tPgo8IS0tICAgYXMudGliYmxlKCkgJT4lIC0tPgo8IS0tICAgbXV0YXRlKGNlbGwxPXRyYW5zZmVyLmFuY2hvcnNAcmVmZXJlbmNlLmNlbGxzW2NlbGwxXSkgJT4lIC0tPgo8IS0tICAgbXV0YXRlKGNlbGwyPXRyYW5zZmVyLmFuY2hvcnNAcXVlcnkuY2VsbHNbY2VsbDJdKSAlPiUgLS0+CjwhLS0gICBtdXRhdGUoYW5uby5jZWxsMSA9IG5ldy5tZXRhZGF0YVtjZWxsMSwgJ2Fubm90YXRpb24nXSkgJT4lIC0tPgo8IS0tICAgbXV0YXRlKGFubm8uY2VsbDIgPSBuZXcubWV0YWRhdGFbY2VsbDIsICdhbm5vdGF0aW9uJ10pICU+JSAtLT4KPCEtLSAgICMgc3ByZWFkKGNlbGwyLCBzY29yZSkgIC0tPgo8IS0tICAgZ2dwbG90KGFlcyhzY29yZSkpICsgLS0+CjwhLS0gICBnZW9tX2hpc3RvZ3JhbSgpICsgLS0+CjwhLS0gICB4bGltKDAsMSkgKyAtLT4KPCEtLSAgICMgZ2VvbV90aWxlKCkgKyAtLT4KPCEtLSAgIGZhY2V0X2dyaWQoYW5uby5jZWxsMX5hbm5vLmNlbGwyLCBzY2FsZXM9ImZyZWVfeSIsIHNwYWNlPSJmcmVlIiwgbGFiZWxsZXIgPSAibGFiZWxfYm90aCIpICAtLT4KPCEtLSBgYGAgLS0+Cgo8IS0tIDwhLS0gc2hvdyBhbmNob3IgbWF0IC0tPiAtLT4KCjwhLS0gPCEtLSBgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfSAtLT4gLS0+CjwhLS0gPCEtLSBhbmNob3IubWF0IDwtIHRyYW5zZmVyLmFuY2hvcnNAYW5jaG9ycyAlPiUgLS0+IC0tPgo8IS0tIDwhLS0gICBhcy50aWJibGUoKSAlPiUgLS0+IC0tPgo8IS0tIDwhLS0gICBtdXRhdGUoY2VsbDE9dHJhbnNmZXIuYW5jaG9yc0ByZWZlcmVuY2UuY2VsbHNbY2VsbDFdKSAlPiUgLS0+IC0tPgo8IS0tIDwhLS0gICBtdXRhdGUoY2VsbDI9dHJhbnNmZXIuYW5jaG9yc0BxdWVyeS5jZWxsc1tjZWxsMl0pICU+JSAtLT4gLS0+CjwhLS0gPCEtLSAgIHNwcmVhZChjZWxsMiwgc2NvcmUpICU+JSAtLT4gLS0+CjwhLS0gPCEtLSAgIGNvbHVtbl90b19yb3duYW1lcygnY2VsbDEnKSAlPiUgLS0+IC0tPgo8IS0tIDwhLS0gICBhcy5tYXRyaXgoKSAtLT4gLS0+Cgo8IS0tIDwhLS0gYW5jaG9yLm1hdCAlPiUgIC0tPiAtLT4KPCEtLSA8IS0tICAgaWZlbHNlKGlzLm5hKC4pLCAwLCAuKSAlPiUgLS0+IC0tPgo8IS0tIDwhLS0gICBwaGVhdG1hcDo6cGhlYXRtYXAoc2hvd19yb3duYW1lcyA9IEYsIHNob3dfY29sbmFtZXMgPSBGLCAtLT4gLS0+CjwhLS0gPCEtLSAgICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IG5ldy5tZXRhZGF0YVssJ2Fubm90YXRpb24nLCBkcm9wPUZdLCAtLT4gLS0+CjwhLS0gPCEtLSAgICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX3JvdyA9IG5ldy5tZXRhZGF0YVssJ2Fubm90YXRpb24nLCBkcm9wPUZdLCAtLT4gLS0+CjwhLS0gPCEtLSAgICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGxpc3QoYW5ub3RhdGlvbj1jZWxsLnR5cGUucGFsKSkgLS0+IC0tPgo8IS0tIDwhLS0gYGBgIC0tPiAtLT4KCjwhLS0gYGBge3J9IC0tPgo8IS0tIHByZWQuc2NvcmVzIDwtIGNvbG5hbWVzKGNvZW1iZWRAbWV0YS5kYXRhKSAlPiUgc3RyX3N1YnNldCgicHJlZGljdGlvbi5zY29yZSIpICAtLT4KPCEtLSBjb2VtYmVkQG1ldGEuZGF0YSAlPiUgLS0+CjwhLS0gICBmaWx0ZXIodGVjaD09IkFUQUMiKSAlPiUgLS0+CjwhLS0gICBzZWxlY3QoYygiYW5ub3RhdGlvbiIsIHByZWQuc2NvcmVzKSkgJT4lIC0tPgo8IS0tICAgcGl2b3RfbG9uZ2VyKGNvbHM9LWFubm90YXRpb24sIG5hbWVzX3RvID0gImNsYXNzIikgJT4lIC0tPgo8IS0tICAgbXV0YXRlKGNsYXNzPXN0cl9yZW1vdmUoY2xhc3MsICJwcmVkaWN0aW9uLnNjb3JlLiIpKSAlPiUgLS0+CjwhLS0gICBmaWx0ZXIoY2xhc3MhPSJtYXgiKSAlPiUgLS0+CjwhLS0gICBnZ3Bsb3QoYWVzKGFubm90YXRpb24sIHZhbHVlLCBmaWxsPWNsYXNzKSkgKyAtLT4KPCEtLSAgIGdlb21fYm94cGxvdCgpIC0tPgo8IS0tIGBgYCAtLT4KCjwhLS0gYGBge3IsIGZpZy53aWR0aD0xMH0gLS0+CjwhLS0gRGVmYXVsdEFzc2F5KGRwcS5jb2VtYmVkKSA8LSAiUk5BIiAtLT4KPCEtLSBkcHEuY29lbWJlZCA8LSBTY2FsZURhdGEoZHBxLmNvZW1iZWQsIGZlYXR1cmVzID0gaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uKSAtLT4KPCEtLSBkcHEuY29lbWJlZCA8LSBSdW5QQ0EoZHBxLmNvZW1iZWQsIGZlYXR1cmVzID0gaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uKSAtLT4KCjwhLS0gcGxvdF9ncmlkKCAtLT4KPCEtLSAgIERpbVBsb3QoZHBxLmNvZW1iZWQsIGdyb3VwLmJ5ID0gYygidGVjaCIpLCByZWR1Y3Rpb24gPSAicGNhIikgLCAtLT4KPCEtLSAgIEZlYXR1cmVQbG90KGRwcS5jb2VtYmVkLCBmZWF0dXJlPSJkcHRfcHNldWRvdGltZSIsIHJlZHVjdGlvbiA9ICJwY2EiKSArIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICAtLT4KPCEtLSAgICkgLS0+CjwhLS0gYGBgIC0tPgo8IS0tIGBgYHtyfSAtLT4KPCEtLSB0b3BQQzEuZGYgPC0gZHBxLmNvZW1iZWRAcmVkdWN0aW9ucyRwY2FAZmVhdHVyZS5sb2FkaW5nc1ssMSwgZHJvcD1GXSAlPiUgLS0+CjwhLS0gICBhcy50aWJibGUocm93bmFtZXM9ImdlbmUiKSAlPiUgLS0+CjwhLS0gICAjIGFycmFuZ2UoUENfMSkgLS0+CjwhLS0gICBtdXRhdGUocmFuaz0gZGVuc2VfcmFuayhQQ18xKSkgJT4lIC0tPgo8IS0tICAgbXV0YXRlKGxhYmVsPWlmZWxzZShyYW5rID4gKG4oKS0xMCkgfCByYW5rIDwgKDEwKSAsIGdlbmUsTkEpKSAgLS0+Cgo8IS0tIHRvcFBDMS5kZiAlPiUgLS0+CjwhLS0gICBnZ3Bsb3QoYWVzKHJhbmssIFBDXzEpKSArIC0tPgo8IS0tICAgZ2VvbV9wb2ludCgpKyAtLT4KPCEtLSAgIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChkYXRhPS4gJT4lIGZpbHRlcighaXMubmEobGFiZWwpKSwgYWVzKGxhYmVsPWxhYmVsKSkgLS0+CjwhLS0gYGBgIC0tPgo8IS0tIGBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04fSAtLT4KPCEtLSBGZWF0dXJlUGxvdChkcHEuY29lbWJlZCwgZmVhdHVyZT11bmlxdWUodG9wUEMxLmRmJGxhYmVsKVsxOjZdLCByZWR1Y3Rpb24gPSAidW1hcCIpICAtLT4KPCEtLSAgIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpIC0tPgo8IS0tIGBgYCAtLT4KCjwhLS0gYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD00fSAtLT4KPCEtLSBjY2Eub2JqQHJlZHVjdGlvbnMkY2NhQGZlYXR1cmUubG9hZGluZ3MgJT4lIC0tPgo8IS0tICAgcmVzaGFwZTIgLS0+CjwhLS0gICBwaGVhdG1hcDo6cGhlYXRtYXAoKSAtLT4KPCEtLSBEaW1IZWF0bWFwKGNjYS5vYmosIHJlZHVjdGlvbiA9ICJjY2EiLCBhc3NheXMgPSAiQVRBQyIpIC0tPgo8IS0tIEZlYXR1cmVQbG90KGNjYS5vYmosIHJlZHVjdGlvbj0iY2NhIiwgZmVhdHVyZT1nZW5lLm9pKSAtLT4KCjwhLS0gYGBgIC0tPgoKCjwhLS0gPCEtLSBEaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgYmV0d2VlbiBSTkEgYW5kIEFUQUMgRFAoUSkgY2VsbHMgLS0+IC0tPgo8IS0tIDwhLS0gYGBge3J9IC0tPiAtLT4KPCEtLSA8IS0tIFZhcmlhYmxlRmVhdHVyZXMoY29lbWJlZCkgPC0gaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uIC0tPiAtLT4KPCEtLSA8IS0tIGRwcS5kaWZmIDwtIEZpbmRNYXJrZXJzKGRwcS5jb2VtYmVkLCBmZWF0dXJlcz1WYXJpYWJsZUZlYXR1cmVzKGNvZW1iZWQpLCAgLS0+IC0tPgo8IS0tIDwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAidGVjaCIsIGlkZW50LjEgPSAiUk5BIiwgaWRlbnQuMiA9ICJBVEFDIikgLS0+IC0tPgoKPCEtLSA8IS0tIHRvcC5kaWZmIDwtIHJvd25hbWVzKGRwcS5kaWZmWzE6MTAsXSkgLS0+IC0tPgo8IS0tIDwhLS0gRmVhdHVyZVBsb3QoY29lbWJlZCwgZmVhdHVyZXMgPSB0b3AuZGlmZlsxOjNdLCBzcGxpdC5ieSA9ICJ0ZWNoIiwgY29scyA9IHZpcmlkaXM6OnZpcmlkaXMobj0xMDApLCBzbG90ID0gInNjYWxlLmRhdGEiLCBtYXguY3V0b2ZmID0gMTApICsgZ2d0aXRsZSgiU3RhZ2UgTWFya2VycyIpIC0tPiAtLT4KPCEtLSA8IS0tIGBgYCAtLT4gLS0+CjwhLS0gPCEtLSBgYGB7cn0gLS0+IC0tPgoKPCEtLSA8IS0tIFZsblBsb3QoY29lbWJlZFssIGNvZW1iZWQkdGVjaD09IlJOQSJdLCBmZWF0dXJlcyA9IHRvcC5kaWZmWzFdLCBncm91cC5ieSA9ICJhbm5vdGF0aW9uIiwgcHQuc2l6ZSA9IDAuMSwgc3BsaXQuYnkgPSAndGVjaCcpIC0tPiAtLT4KPCEtLSA8IS0tIFZsblBsb3QoY29lbWJlZCwgZmVhdHVyZXMgPSBjKCJBUVAzIiwgIlRSQkMyIiksIGdyb3VwLmJ5ID0gInRlY2giKSAtLT4gLS0+CjwhLS0gPCEtLSBgYGAgLS0+IC0tPgoKPCEtLSBgYGB7ciwgZmlnLndpZHRoPTEyfSAtLT4KPCEtLSBhdGFjLm1hdCA8LSBjb2VtYmVkQGFzc2F5cyRBVEFDQGRhdGEgLS0+CjwhLS0gcm5hLm1hdCA8LSBjb2VtYmVkQGFzc2F5cyRSTkFAZGF0YSAtLT4KCjwhLS0gZ2VuZS5vaSA8LSB1bmlxdWUodG9wUEMxLmRmJGxhYmVsKSAlPiUgc3RyX3N1YnNldCgiVFJBViIpIC0tPgo8IS0tIGdlbmUub2kgLS0+CjwhLS0gYXRhYy5kZiA8LSBhdGFjLm1hdFtnZW5lLm9pLCwgZHJvcD1GXSAlPiUgLS0+CjwhLS0gICBhcy5tYXRyaXgoKSAlPiUgLS0+CjwhLS0gICByZXNoYXBlMjo6bWVsdCh2YXJuYW1lcz1jKCJnZW5lIiwgImNlbGwiKSkgJT4lIC0tPgo8IS0tICAgbXV0YXRlKHRlY2g9IkFUQUMiKSAlPiUgLS0+CjwhLS0gICBsZWZ0X2pvaW4oZHB0LmRmLCBieT1jKCdjZWxsJywgInRlY2giKSkgIC0tPgo8IS0tICMgJT4lIC0tPgo8IS0tICMgICBncm91cF9ieSh0ZWNoLCBkcHRfYmluLCBnZW5lKSAlPiUgLS0+CjwhLS0gIyAgIHN1bW1hcmlzZShmcmFjPXN1bSh2YWx1ZSE9MCkvbigpKSAtLT4KCjwhLS0gcm5hLmRmIDwtIHJuYS5tYXRbZ2VuZS5vaSwsIGRyb3A9Rl0gJT4lIC0tPgo8IS0tICAgYXMubWF0cml4KCkgJT4lIC0tPgo8IS0tICAgcmVzaGFwZTI6Om1lbHQodmFybmFtZXM9YygiZ2VuZSIsICJjZWxsIikpICU+JSAtLT4KPCEtLSAgIGZpbHRlcihzdHJfZGV0ZWN0KGNlbGwsICJSTkFfIikpICU+JSAtLT4KPCEtLSAgIG11dGF0ZSh0ZWNoPSJSTkEiKSAlPiUgLS0+CjwhLS0gICBsZWZ0X2pvaW4oZHB0LmRmLCBieT1jKCdjZWxsJywgInRlY2giKSkgLS0+CgoKPCEtLSBhY2MucGwgPC0gYXRhYy5kZiAlPiUgLS0+CjwhLS0gICBkcm9wX25hKGRwdF9iaW4pICU+JSAtLT4KPCEtLSAgIGdncGxvdChhZXMoZHB0X2JpbiwgZmlsbD1hcy5mYWN0b3IodmFsdWUpKSkgKyAtLT4KPCEtLSAgIGdlb21fYmFyKCkgIC0tPgo8IS0tICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNlbGwudHlwZS5wYWwsIG5hLnZhbHVlPSJncmV5NTAiKSAtLT4KPCEtLSBhY2MucGwgLS0+CjwhLS0gZXgucGwgPC0gcm5hLmRmICU+JSAtLT4KPCEtLSAgIGZpbHRlcihnZW5lPT1nZW5lLm9pKSAlPiUgLS0+CjwhLS0gICAjIG11dGF0ZSh2YWx1ZT1pZmVsc2UodmFsdWU+MCwxLDApKSAlPiUgLS0+CjwhLS0gICAjIGZpbHRlcih2YWx1ZSE9MCkgJT4lIC0tPgo8IS0tICAgZ2dwbG90KGFlcyhkcHRfYmluLCB2YWx1ZSkpICsgLS0+CjwhLS0gICAjIGdlb21fdmlvbGluKGFscGhhPTAuMikgKyAtLT4KPCEtLSAgICMgZ2VvbV9wb2ludCgpIC0tPgo8IS0tICAgIyBzY2FsZV9maWxsX3ZpcmlkaXNfZCgpIC0tPgo8IS0tICAgIyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Y2VsbC50eXBlLnBhbCkgLS0+CjwhLS0gICBnZW9tX2ppdHRlcihhbHBoYT0wLjUsIHNpemU9MC41KSArIC0tPgo8IS0tICAgZ2VvbV9zbW9vdGgoKSAgLS0+CjwhLS0gICB4bGltKDAsNTApIC0tPgoKCjwhLS0gcGxvdF9ncmlkKGFjYy5wbCwgZXgucGwsIG5jb2w9MSwgbnJvdz0yLCBhbGlnbiA9ICJ2IiwgYXhpcy49ImwiKSAtLT4KPCEtLSBgYGAgLS0+Cgo8IS0tIGBgYHtyfSAtLT4KPCEtLSBhdGFjLmRmICU+JSAtLT4KPCEtLSAgIGRyb3BfbmEoZHB0X3BzZXVkb3RpbWUpICU+JSAtLT4KPCEtLSAgIGdyb3VwX2J5KCBnZW5lLCBkcHRfYmluKSAlPiUgLS0+CjwhLS0gICAjIHN1bW1hcmlzZShmcmFjPXN1bSh2YWx1ZSkvbigpKSAlPiUgLS0+CjwhLS0gICBnZ3Bsb3QoYWVzKGRwdF9iaW4sIHZhbHVlLCBjb2xvcj1nZW5lKSkgKyAtLT4KPCEtLSAgICMgZ2VvbV9wb2ludCgpICsgLS0+CjwhLS0gICBnZW9tX3Ntb290aCgpIC0tPgo8IS0tIGBgYCAtLT4KCi0tLQoKCgoKCgoK